@concord-consortium/object-storage 1.0.0-pre.5 → 1.0.0-pre.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/demo-object-storage.d.ts +12 -29
- package/dist/demo-object-storage.js +29 -80
- package/dist/firebase-object-storage.d.ts +19 -27
- package/dist/firebase-object-storage.js +125 -72
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/stored-object.d.ts +74 -0
- package/dist/stored-object.js +101 -0
- package/dist/typed-object.d.ts +1 -1
- package/dist/types.d.ts +20 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,7 +39,9 @@ function YourComponent() {
|
|
|
39
39
|
|
|
40
40
|
// Use storage methods
|
|
41
41
|
const handleAdd = async () => {
|
|
42
|
-
const
|
|
42
|
+
const object = new StoredObject();
|
|
43
|
+
const result = await storage.add(object);
|
|
44
|
+
console.log('Added object with id:', result.id);
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
return <div>Your component</div>;
|
|
@@ -1,44 +1,26 @@
|
|
|
1
|
-
import { DemoObjectStorageConfig, IObjectStorage,
|
|
1
|
+
import { DemoObjectStorageConfig, IObjectStorage, MonitorCallback, DemonitorFunction, StoredObjectMetadataWithId } from './types';
|
|
2
|
+
import { StoredObject, StoredObjectMetadata, StoredObjectData } from './stored-object';
|
|
2
3
|
export declare class DemoObjectStorage implements IObjectStorage {
|
|
3
4
|
private config;
|
|
4
5
|
private objects;
|
|
5
6
|
private monitors;
|
|
6
7
|
constructor(config: DemoObjectStorageConfig);
|
|
7
|
-
|
|
8
|
-
* Lists metadata documents for objects owned by the current user
|
|
9
|
-
*/
|
|
10
|
-
listMine(): Promise<ObjectWithId[]>;
|
|
11
|
-
/**
|
|
12
|
-
* Lists metadata documents for objects linked to the current user
|
|
13
|
-
*/
|
|
14
|
-
listLinked(): Promise<ObjectWithId[]>;
|
|
8
|
+
private getQuestionMetadata;
|
|
15
9
|
/**
|
|
16
10
|
* Lists metadata documents for objects associated with specific question IDs
|
|
17
11
|
*/
|
|
18
|
-
list(
|
|
19
|
-
/**
|
|
20
|
-
* Monitors metadata documents for objects owned by the current user
|
|
21
|
-
* Invokes callback at start and on any change
|
|
22
|
-
* Returns a function to stop monitoring
|
|
23
|
-
*/
|
|
24
|
-
monitorMine(callback: MonitorCallback): DemonitorFunction;
|
|
25
|
-
/**
|
|
26
|
-
* Monitors metadata documents for objects linked to the current user
|
|
27
|
-
* Invokes callback at start and on any change
|
|
28
|
-
* Returns a function to stop monitoring
|
|
29
|
-
*/
|
|
30
|
-
monitorLinked(callback: MonitorCallback): DemonitorFunction;
|
|
12
|
+
list(questionId: string): Promise<StoredObjectMetadataWithId[]>;
|
|
31
13
|
/**
|
|
32
14
|
* Monitors metadata documents for objects associated with specific question IDs
|
|
33
15
|
* Invokes callback at start and on any change
|
|
34
16
|
* Returns a function to stop monitoring
|
|
35
17
|
*/
|
|
36
|
-
monitor(
|
|
18
|
+
monitor(questionId: string, callback: MonitorCallback): DemonitorFunction;
|
|
37
19
|
/**
|
|
38
20
|
* Adds both metadata and data documents for a new object
|
|
39
|
-
* Returns the
|
|
21
|
+
* Returns the StoredObject that was added
|
|
40
22
|
*/
|
|
41
|
-
add(object: StoredObject
|
|
23
|
+
add(object: StoredObject): Promise<StoredObject>;
|
|
42
24
|
/**
|
|
43
25
|
* Reads both metadata and data documents for an object
|
|
44
26
|
* Returns undefined if the object is not found
|
|
@@ -48,16 +30,17 @@ export declare class DemoObjectStorage implements IObjectStorage {
|
|
|
48
30
|
* Reads only the metadata document for an object
|
|
49
31
|
* Returns undefined if the object is not found
|
|
50
32
|
*/
|
|
51
|
-
readMetadata(objectId: string): Promise<
|
|
33
|
+
readMetadata(objectId: string): Promise<StoredObjectMetadata | undefined>;
|
|
52
34
|
/**
|
|
53
35
|
* Reads only the data document for an object
|
|
54
36
|
* Returns undefined if the object is not found
|
|
55
37
|
*/
|
|
56
|
-
readData(objectId: string): Promise<
|
|
38
|
+
readData(objectId: string): Promise<StoredObjectData | undefined>;
|
|
57
39
|
/**
|
|
58
|
-
*
|
|
40
|
+
* Reads a single data item for an object
|
|
41
|
+
* Returns undefined if the object or item is not found
|
|
59
42
|
*/
|
|
60
|
-
|
|
43
|
+
readDataItem(objectId: string, itemId: string): Promise<any | undefined>;
|
|
61
44
|
/**
|
|
62
45
|
* Notifies all active monitors of changes
|
|
63
46
|
*/
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DemoObjectStorage = void 0;
|
|
4
|
-
const nanoid_1 = require("nanoid");
|
|
5
4
|
class DemoObjectStorage {
|
|
6
5
|
constructor(config) {
|
|
7
6
|
if (config.version !== 1) {
|
|
@@ -11,76 +10,33 @@ class DemoObjectStorage {
|
|
|
11
10
|
this.objects = new Map();
|
|
12
11
|
this.monitors = new Map();
|
|
13
12
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Lists metadata documents for objects linked to the current user
|
|
22
|
-
*/
|
|
23
|
-
async listLinked() {
|
|
24
|
-
// not applicable in demo storage
|
|
25
|
-
return [];
|
|
13
|
+
getQuestionMetadata() {
|
|
14
|
+
// In demo mode, just return all objects since there are no other questions
|
|
15
|
+
// that can use this storage instance
|
|
16
|
+
return Array.from(this.objects.entries()).map(([id, obj]) => {
|
|
17
|
+
return { id, metadata: obj.metadata };
|
|
18
|
+
});
|
|
26
19
|
}
|
|
27
20
|
/**
|
|
28
21
|
* Lists metadata documents for objects associated with specific question IDs
|
|
29
22
|
*/
|
|
30
|
-
async list(
|
|
31
|
-
|
|
32
|
-
return Array.from(this.objects.values());
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Monitors metadata documents for objects owned by the current user
|
|
36
|
-
* Invokes callback at start and on any change
|
|
37
|
-
* Returns a function to stop monitoring
|
|
38
|
-
*/
|
|
39
|
-
monitorMine(callback) {
|
|
40
|
-
const key = 'mine';
|
|
41
|
-
if (!this.monitors.has(key)) {
|
|
42
|
-
this.monitors.set(key, []);
|
|
43
|
-
}
|
|
44
|
-
this.monitors.get(key).push(callback);
|
|
45
|
-
// Invoke callback immediately with current state
|
|
46
|
-
callback(Array.from(this.objects.values()));
|
|
47
|
-
return () => {
|
|
48
|
-
const callbacks = this.monitors.get(key);
|
|
49
|
-
if (callbacks) {
|
|
50
|
-
const index = callbacks.indexOf(callback);
|
|
51
|
-
if (index > -1) {
|
|
52
|
-
callbacks.splice(index, 1);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Monitors metadata documents for objects linked to the current user
|
|
59
|
-
* Invokes callback at start and on any change
|
|
60
|
-
* Returns a function to stop monitoring
|
|
61
|
-
*/
|
|
62
|
-
monitorLinked(callback) {
|
|
63
|
-
// not applicable in demo storage
|
|
64
|
-
callback([]);
|
|
65
|
-
return () => {
|
|
66
|
-
// no-op
|
|
67
|
-
};
|
|
23
|
+
async list(questionId) {
|
|
24
|
+
return this.getQuestionMetadata();
|
|
68
25
|
}
|
|
69
26
|
/**
|
|
70
27
|
* Monitors metadata documents for objects associated with specific question IDs
|
|
71
28
|
* Invokes callback at start and on any change
|
|
72
29
|
* Returns a function to stop monitoring
|
|
73
30
|
*/
|
|
74
|
-
monitor(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.monitors.set(key, []);
|
|
31
|
+
monitor(questionId, callback) {
|
|
32
|
+
if (!this.monitors.has(questionId)) {
|
|
33
|
+
this.monitors.set(questionId, []);
|
|
78
34
|
}
|
|
79
|
-
this.monitors.get(
|
|
35
|
+
this.monitors.get(questionId).push(callback);
|
|
80
36
|
// Invoke callback immediately with current state
|
|
81
|
-
callback(
|
|
37
|
+
callback(this.getQuestionMetadata());
|
|
82
38
|
return () => {
|
|
83
|
-
const callbacks = this.monitors.get(
|
|
39
|
+
const callbacks = this.monitors.get(questionId);
|
|
84
40
|
if (callbacks) {
|
|
85
41
|
const index = callbacks.indexOf(callback);
|
|
86
42
|
if (index > -1) {
|
|
@@ -91,33 +47,21 @@ class DemoObjectStorage {
|
|
|
91
47
|
}
|
|
92
48
|
/**
|
|
93
49
|
* Adds both metadata and data documents for a new object
|
|
94
|
-
* Returns the
|
|
50
|
+
* Returns the StoredObject that was added
|
|
95
51
|
*/
|
|
96
|
-
async add(object
|
|
97
|
-
const id =
|
|
98
|
-
|
|
99
|
-
id,
|
|
100
|
-
metadata: object.metadata,
|
|
101
|
-
data: object.data
|
|
102
|
-
};
|
|
103
|
-
this.objects.set(id, objectWithId);
|
|
52
|
+
async add(object) {
|
|
53
|
+
const id = object.id;
|
|
54
|
+
this.objects.set(id, object);
|
|
104
55
|
// Notify all monitors
|
|
105
56
|
this.notifyMonitors();
|
|
106
|
-
return
|
|
57
|
+
return object;
|
|
107
58
|
}
|
|
108
59
|
/**
|
|
109
60
|
* Reads both metadata and data documents for an object
|
|
110
61
|
* Returns undefined if the object is not found
|
|
111
62
|
*/
|
|
112
63
|
async read(objectId) {
|
|
113
|
-
|
|
114
|
-
if (!obj) {
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
metadata: obj.metadata,
|
|
119
|
-
data: obj.data
|
|
120
|
-
};
|
|
64
|
+
return this.objects.get(objectId);
|
|
121
65
|
}
|
|
122
66
|
/**
|
|
123
67
|
* Reads only the metadata document for an object
|
|
@@ -142,16 +86,21 @@ class DemoObjectStorage {
|
|
|
142
86
|
return obj.data;
|
|
143
87
|
}
|
|
144
88
|
/**
|
|
145
|
-
*
|
|
89
|
+
* Reads a single data item for an object
|
|
90
|
+
* Returns undefined if the object or item is not found
|
|
146
91
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
92
|
+
async readDataItem(objectId, itemId) {
|
|
93
|
+
const obj = this.objects.get(objectId);
|
|
94
|
+
if (!obj) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
return obj.data[itemId];
|
|
149
98
|
}
|
|
150
99
|
/**
|
|
151
100
|
* Notifies all active monitors of changes
|
|
152
101
|
*/
|
|
153
102
|
notifyMonitors() {
|
|
154
|
-
const allObjects =
|
|
103
|
+
const allObjects = this.getQuestionMetadata();
|
|
155
104
|
this.monitors.forEach(callbacks => {
|
|
156
105
|
callbacks.forEach(callback => callback(allObjects));
|
|
157
106
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "firebase/compat/auth";
|
|
2
2
|
import "firebase/compat/firestore";
|
|
3
|
-
import { FirebaseObjectStorageConfig, IObjectStorage,
|
|
3
|
+
import { FirebaseObjectStorageConfig, IObjectStorage, MonitorCallback, DemonitorFunction, StoredObjectMetadataWithId } from './types';
|
|
4
|
+
import { StoredObject, StoredObjectMetadata, StoredObjectData } from './stored-object';
|
|
4
5
|
export declare class FirebaseObjectStorage implements IObjectStorage {
|
|
5
6
|
private config;
|
|
6
7
|
private initPromise;
|
|
@@ -12,41 +13,32 @@ export declare class FirebaseObjectStorage implements IObjectStorage {
|
|
|
12
13
|
private createDocument;
|
|
13
14
|
private getPaths;
|
|
14
15
|
private getRefs;
|
|
16
|
+
private getItemDataPath;
|
|
17
|
+
private getMetadataQuery;
|
|
15
18
|
/**
|
|
16
|
-
*
|
|
19
|
+
* Ensures the provided ID is in question ID format as opposed to reference ID format
|
|
20
|
+
*
|
|
21
|
+
* eg: "404-MWInteractive" transforms to "mw_interactive_404"
|
|
22
|
+
*
|
|
23
|
+
* @param questionOrRefId
|
|
24
|
+
* @returns
|
|
17
25
|
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Lists metadata documents for objects linked to the current user
|
|
21
|
-
*/
|
|
22
|
-
listLinked(): Promise<ObjectWithId[]>;
|
|
26
|
+
private ensureIdIsQuestionId;
|
|
23
27
|
/**
|
|
24
28
|
* Lists metadata documents for objects associated with specific question IDs
|
|
25
29
|
*/
|
|
26
|
-
list(
|
|
27
|
-
/**
|
|
28
|
-
* Monitors metadata documents for objects owned by the current user
|
|
29
|
-
* Invokes callback at start and on any change
|
|
30
|
-
* Returns a function to stop monitoring
|
|
31
|
-
*/
|
|
32
|
-
monitorMine(callback: MonitorCallback): DemonitorFunction;
|
|
33
|
-
/**
|
|
34
|
-
* Monitors metadata documents for objects linked to the current user
|
|
35
|
-
* Invokes callback at start and on any change
|
|
36
|
-
* Returns a function to stop monitoring
|
|
37
|
-
*/
|
|
38
|
-
monitorLinked(callback: MonitorCallback): DemonitorFunction;
|
|
30
|
+
list(questionOrRefId: string): Promise<StoredObjectMetadataWithId[]>;
|
|
39
31
|
/**
|
|
40
32
|
* Monitors metadata documents for objects associated with specific question IDs
|
|
41
33
|
* Invokes callback at start and on any change
|
|
42
34
|
* Returns a function to stop monitoring
|
|
43
35
|
*/
|
|
44
|
-
monitor(
|
|
36
|
+
monitor(questionOrRefId: string, callback: MonitorCallback): DemonitorFunction;
|
|
45
37
|
/**
|
|
46
38
|
* Adds both metadata and data documents for a new object
|
|
47
|
-
* Returns the
|
|
39
|
+
* Returns the StoredObject that was added
|
|
48
40
|
*/
|
|
49
|
-
add(object: StoredObject
|
|
41
|
+
add(object: StoredObject): Promise<StoredObject>;
|
|
50
42
|
/**
|
|
51
43
|
* Reads both metadata and data documents for an object
|
|
52
44
|
*/
|
|
@@ -54,13 +46,13 @@ export declare class FirebaseObjectStorage implements IObjectStorage {
|
|
|
54
46
|
/**
|
|
55
47
|
* Reads only the metadata document for an object
|
|
56
48
|
*/
|
|
57
|
-
readMetadata(objectId: string): Promise<
|
|
49
|
+
readMetadata(objectId: string): Promise<StoredObjectMetadata | undefined>;
|
|
58
50
|
/**
|
|
59
51
|
* Reads only the data document for an object
|
|
60
52
|
*/
|
|
61
|
-
readData(objectId: string): Promise<
|
|
53
|
+
readData(objectId: string): Promise<StoredObjectData | undefined>;
|
|
62
54
|
/**
|
|
63
|
-
*
|
|
55
|
+
* Reads a single data item for an object
|
|
64
56
|
*/
|
|
65
|
-
|
|
57
|
+
readDataItem(objectId: string, itemId: string): Promise<any | undefined>;
|
|
66
58
|
}
|
|
@@ -4,17 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.FirebaseObjectStorage = void 0;
|
|
7
|
-
const nanoid_1 = require("nanoid");
|
|
8
7
|
const app_1 = __importDefault(require("firebase/compat/app"));
|
|
9
8
|
require("firebase/compat/auth");
|
|
10
9
|
require("firebase/compat/firestore");
|
|
10
|
+
const stored_object_1 = require("./stored-object");
|
|
11
11
|
class FirebaseObjectStorage {
|
|
12
12
|
constructor(config) {
|
|
13
13
|
this.initialized = false;
|
|
14
14
|
if (config.version !== 1) {
|
|
15
15
|
throw new Error(`Unsupported config version: ${config.version}. Expected version 1.`);
|
|
16
16
|
}
|
|
17
|
-
this.config = config;
|
|
17
|
+
this.config = { ...config };
|
|
18
|
+
// ensure the question id is in the correct format
|
|
19
|
+
this.config.questionId = this.ensureIdIsQuestionId(this.config.questionId);
|
|
18
20
|
this.app = app_1.default.initializeApp(this.config.app);
|
|
19
21
|
this.app.firestore().settings({
|
|
20
22
|
ignoreUndefinedProperties: true,
|
|
@@ -36,15 +38,17 @@ class FirebaseObjectStorage {
|
|
|
36
38
|
await this.initPromise;
|
|
37
39
|
}
|
|
38
40
|
createDocument(contents) {
|
|
41
|
+
const { questionId } = this.config;
|
|
39
42
|
if (this.config.user.type === "authenticated") {
|
|
40
43
|
const { contextId, platformId, platformUserId, resourceLinkId } = this.config.user;
|
|
41
44
|
return {
|
|
42
45
|
created_at: app_1.default.firestore.FieldValue.serverTimestamp(),
|
|
43
|
-
platform_id: platformId,
|
|
44
|
-
platform_user_id: platformUserId.toString(),
|
|
45
46
|
context_id: contextId,
|
|
47
|
+
platform_id: platformId,
|
|
48
|
+
platform_user_id: platformUserId,
|
|
46
49
|
resource_link_id: resourceLinkId,
|
|
47
50
|
run_key: "",
|
|
51
|
+
question_id: questionId,
|
|
48
52
|
...contents
|
|
49
53
|
};
|
|
50
54
|
}
|
|
@@ -54,13 +58,14 @@ class FirebaseObjectStorage {
|
|
|
54
58
|
created_at: app_1.default.firestore.FieldValue.serverTimestamp(),
|
|
55
59
|
run_key: runKey,
|
|
56
60
|
platform_user_id: runKey,
|
|
61
|
+
question_id: questionId,
|
|
57
62
|
...contents
|
|
58
63
|
};
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
getPaths(objectId) {
|
|
62
|
-
const metadataPath = `${this.config.root}/object_store_metadata
|
|
63
|
-
const dataPath = `${this.config.root}/object_store_data
|
|
67
|
+
const metadataPath = `${this.config.root}/object_store_metadata${objectId ? `/${objectId}` : ''}`;
|
|
68
|
+
const dataPath = `${this.config.root}/object_store_data${objectId ? `/${objectId}` : ''}`;
|
|
64
69
|
return { metadataPath, dataPath };
|
|
65
70
|
}
|
|
66
71
|
getRefs(objectId) {
|
|
@@ -69,119 +74,167 @@ class FirebaseObjectStorage {
|
|
|
69
74
|
const dataRef = this.app.firestore().doc(dataPath);
|
|
70
75
|
return { metadataRef, dataRef };
|
|
71
76
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
getItemDataPath(objectId, itemId) {
|
|
78
|
+
return `${this.config.root}/object_store_data/${objectId}-${itemId}`;
|
|
79
|
+
}
|
|
80
|
+
getMetadataQuery(questionOrRefId) {
|
|
81
|
+
const questionId = this.ensureIdIsQuestionId(questionOrRefId);
|
|
82
|
+
const { metadataPath } = this.getPaths();
|
|
83
|
+
let query = this.app.firestore().collection(metadataPath)
|
|
84
|
+
.where("question_id", "==", questionId);
|
|
85
|
+
if (this.config.user.type === "authenticated") { // logged in user
|
|
86
|
+
const { contextId, platformId, resourceLinkId, platformUserId } = this.config.user;
|
|
87
|
+
query = query
|
|
88
|
+
.where("context_id", "==", contextId)
|
|
89
|
+
.where("platform_id", "==", platformId)
|
|
90
|
+
.where("platform_user_id", "==", platformUserId.toString())
|
|
91
|
+
.where("resource_link_id", "==", resourceLinkId);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
query = query.where("run_key", "==", this.config.user.runKey);
|
|
95
|
+
}
|
|
96
|
+
query = query.orderBy("created_at", "asc");
|
|
97
|
+
return query;
|
|
79
98
|
}
|
|
80
99
|
/**
|
|
81
|
-
*
|
|
100
|
+
* Ensures the provided ID is in question ID format as opposed to reference ID format
|
|
101
|
+
*
|
|
102
|
+
* eg: "404-MWInteractive" transforms to "mw_interactive_404"
|
|
103
|
+
*
|
|
104
|
+
* @param questionOrRefId
|
|
105
|
+
* @returns
|
|
82
106
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
107
|
+
ensureIdIsQuestionId(questionOrRefId) {
|
|
108
|
+
const refIdRegEx = /(\d*)-(\D*)/g;
|
|
109
|
+
const parsed = refIdRegEx.exec(questionOrRefId);
|
|
110
|
+
if (parsed?.length) {
|
|
111
|
+
const [, embeddableId, embeddableType] = parsed;
|
|
112
|
+
const snakeCased = embeddableType.replace(/(?!^)([A-Z])/g, "_$1").toLowerCase();
|
|
113
|
+
return `${snakeCased}_${embeddableId}`;
|
|
114
|
+
}
|
|
115
|
+
return questionOrRefId;
|
|
87
116
|
}
|
|
88
117
|
/**
|
|
89
118
|
* Lists metadata documents for objects associated with specific question IDs
|
|
90
119
|
*/
|
|
91
|
-
async list(
|
|
120
|
+
async list(questionOrRefId) {
|
|
92
121
|
await this.ensureInitialized();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*/
|
|
101
|
-
monitorMine(callback) {
|
|
102
|
-
// await this.ensureInitialized();
|
|
103
|
-
// TODO: Implement Firebase realtime listener for user's own objects
|
|
104
|
-
callback([]);
|
|
105
|
-
return () => {
|
|
106
|
-
// TODO: Implement cleanup
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Monitors metadata documents for objects linked to the current user
|
|
111
|
-
* Invokes callback at start and on any change
|
|
112
|
-
* Returns a function to stop monitoring
|
|
113
|
-
*/
|
|
114
|
-
monitorLinked(callback) {
|
|
115
|
-
// await this.ensureInitialized();
|
|
116
|
-
// TODO: Implement Firebase realtime listener for linked objects
|
|
117
|
-
callback([]);
|
|
118
|
-
return () => {
|
|
119
|
-
// TODO: Implement cleanup
|
|
120
|
-
};
|
|
122
|
+
const query = this.getMetadataQuery(questionOrRefId);
|
|
123
|
+
const querySnapshot = await query.get();
|
|
124
|
+
const results = [];
|
|
125
|
+
querySnapshot.forEach(doc => {
|
|
126
|
+
results.push({ id: doc.id, metadata: doc.data().metadata });
|
|
127
|
+
});
|
|
128
|
+
return results;
|
|
121
129
|
}
|
|
122
130
|
/**
|
|
123
131
|
* Monitors metadata documents for objects associated with specific question IDs
|
|
124
132
|
* Invokes callback at start and on any change
|
|
125
133
|
* Returns a function to stop monitoring
|
|
126
134
|
*/
|
|
127
|
-
monitor(
|
|
135
|
+
monitor(questionOrRefId, callback) {
|
|
128
136
|
// await this.ensureInitialized();
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
const query = this.getMetadataQuery(questionOrRefId);
|
|
138
|
+
const unsub = query.onSnapshot(snapshot => {
|
|
139
|
+
const results = [];
|
|
140
|
+
snapshot.forEach(doc => {
|
|
141
|
+
results.push({ id: doc.id, metadata: doc.data().metadata });
|
|
142
|
+
});
|
|
143
|
+
callback(results);
|
|
144
|
+
});
|
|
131
145
|
return () => {
|
|
132
|
-
|
|
146
|
+
unsub();
|
|
133
147
|
};
|
|
134
148
|
}
|
|
135
149
|
/**
|
|
136
150
|
* Adds both metadata and data documents for a new object
|
|
137
|
-
* Returns the
|
|
151
|
+
* Returns the StoredObject that was added
|
|
138
152
|
*/
|
|
139
|
-
async add(object
|
|
153
|
+
async add(object) {
|
|
140
154
|
await this.ensureInitialized();
|
|
141
|
-
const newObjectId =
|
|
142
|
-
const {
|
|
143
|
-
const { metadataRef
|
|
144
|
-
const dataDoc = this.createDocument({ data });
|
|
155
|
+
const newObjectId = object.id;
|
|
156
|
+
const { metadata } = object;
|
|
157
|
+
const { metadataRef } = this.getRefs(newObjectId);
|
|
145
158
|
const metadataDoc = this.createDocument({ metadata });
|
|
146
159
|
const batch = this.app.firestore().batch();
|
|
147
|
-
batch.set(dataRef, dataDoc);
|
|
148
160
|
batch.set(metadataRef, metadataDoc);
|
|
161
|
+
// Write each item as a separate document
|
|
162
|
+
for (const itemId in object.data) {
|
|
163
|
+
const itemPath = this.getItemDataPath(newObjectId, itemId);
|
|
164
|
+
const itemRef = this.app.firestore().doc(itemPath);
|
|
165
|
+
const itemDoc = this.createDocument({
|
|
166
|
+
objectId: newObjectId,
|
|
167
|
+
itemId: itemId,
|
|
168
|
+
itemData: JSON.stringify(object.data[itemId])
|
|
169
|
+
});
|
|
170
|
+
batch.set(itemRef, itemDoc);
|
|
171
|
+
}
|
|
149
172
|
await batch.commit();
|
|
150
|
-
return
|
|
173
|
+
return object;
|
|
151
174
|
}
|
|
152
175
|
/**
|
|
153
176
|
* Reads both metadata and data documents for an object
|
|
154
177
|
*/
|
|
155
178
|
async read(objectId) {
|
|
156
179
|
await this.ensureInitialized();
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
180
|
+
const metadata = await this.readMetadata(objectId);
|
|
181
|
+
if (!metadata) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const data = await this.readData(objectId);
|
|
185
|
+
if (!data) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
return stored_object_1.StoredObject.FromParts(objectId, metadata, data);
|
|
163
189
|
}
|
|
164
190
|
/**
|
|
165
191
|
* Reads only the metadata document for an object
|
|
166
192
|
*/
|
|
167
193
|
async readMetadata(objectId) {
|
|
168
194
|
await this.ensureInitialized();
|
|
169
|
-
|
|
170
|
-
|
|
195
|
+
const { metadataRef } = this.getRefs(objectId);
|
|
196
|
+
const metadataSnapshot = await metadataRef.get();
|
|
197
|
+
if (!metadataSnapshot.exists) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
return metadataSnapshot.data()?.metadata;
|
|
171
201
|
}
|
|
172
202
|
/**
|
|
173
203
|
* Reads only the data document for an object
|
|
174
204
|
*/
|
|
175
205
|
async readData(objectId) {
|
|
176
206
|
await this.ensureInitialized();
|
|
177
|
-
|
|
178
|
-
|
|
207
|
+
const dataPath = `${this.config.root}/object_store_data`;
|
|
208
|
+
const query = this.app.firestore().collection(dataPath)
|
|
209
|
+
.where("objectId", "==", objectId);
|
|
210
|
+
const querySnapshot = await query.get();
|
|
211
|
+
if (querySnapshot.empty) {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
const data = {};
|
|
215
|
+
querySnapshot.forEach(doc => {
|
|
216
|
+
const docData = doc.data();
|
|
217
|
+
const itemId = docData.itemId;
|
|
218
|
+
const itemDataString = docData.itemData;
|
|
219
|
+
if (itemId && itemDataString) {
|
|
220
|
+
data[itemId] = JSON.parse(itemDataString);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
return data;
|
|
179
224
|
}
|
|
180
225
|
/**
|
|
181
|
-
*
|
|
226
|
+
* Reads a single data item for an object
|
|
182
227
|
*/
|
|
183
|
-
|
|
184
|
-
|
|
228
|
+
async readDataItem(objectId, itemId) {
|
|
229
|
+
await this.ensureInitialized();
|
|
230
|
+
const itemPath = this.getItemDataPath(objectId, itemId);
|
|
231
|
+
const itemRef = this.app.firestore().doc(itemPath);
|
|
232
|
+
const itemSnapshot = await itemRef.get();
|
|
233
|
+
if (!itemSnapshot.exists) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
const itemDataString = itemSnapshot.data()?.itemData;
|
|
237
|
+
return itemDataString ? JSON.parse(itemDataString) : undefined;
|
|
185
238
|
}
|
|
186
239
|
}
|
|
187
240
|
exports.FirebaseObjectStorage = FirebaseObjectStorage;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ export { DemoObjectStorage } from './demo-object-storage';
|
|
|
2
2
|
export { FirebaseObjectStorage } from './firebase-object-storage';
|
|
3
3
|
export { createObjectStorage } from './object-storage';
|
|
4
4
|
export { ObjectStorageProvider, useObjectStorage } from './object-storage-context';
|
|
5
|
-
export { IObjectStorage, ObjectStorageConfig, DemoObjectStorageConfig, FirebaseObjectStorageUser, FirebaseObjectStorageConfig,
|
|
6
|
-
export * from './
|
|
5
|
+
export { IObjectStorage, ObjectStorageConfig, DemoObjectStorageConfig, FirebaseObjectStorageUser, FirebaseObjectStorageConfig, StoredObjectWithId as ObjectWithId, MonitorCallback, DemonitorFunction } from './types';
|
|
6
|
+
export * from './stored-object';
|
package/dist/index.js
CHANGED
|
@@ -24,4 +24,4 @@ Object.defineProperty(exports, "createObjectStorage", { enumerable: true, get: f
|
|
|
24
24
|
var object_storage_context_1 = require("./object-storage-context");
|
|
25
25
|
Object.defineProperty(exports, "ObjectStorageProvider", { enumerable: true, get: function () { return object_storage_context_1.ObjectStorageProvider; } });
|
|
26
26
|
Object.defineProperty(exports, "useObjectStorage", { enumerable: true, get: function () { return object_storage_context_1.useObjectStorage; } });
|
|
27
|
-
__exportStar(require("./
|
|
27
|
+
__exportStar(require("./stored-object"), exports);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type AddImageOptions = {
|
|
2
|
+
id?: string;
|
|
3
|
+
name: string;
|
|
4
|
+
url: string;
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
description?: string;
|
|
8
|
+
subType?: string;
|
|
9
|
+
};
|
|
10
|
+
export type AddDataTableOptions = {
|
|
11
|
+
id?: string;
|
|
12
|
+
name: string;
|
|
13
|
+
cols: string[];
|
|
14
|
+
rows: any[][];
|
|
15
|
+
description?: string;
|
|
16
|
+
subType?: string;
|
|
17
|
+
};
|
|
18
|
+
export type AddTextOptions = {
|
|
19
|
+
id?: string;
|
|
20
|
+
name: string;
|
|
21
|
+
text: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
subType?: string;
|
|
24
|
+
};
|
|
25
|
+
export type AddStoredObjectOptions = {
|
|
26
|
+
id?: string;
|
|
27
|
+
name: string;
|
|
28
|
+
data: Record<string, any>;
|
|
29
|
+
description?: string;
|
|
30
|
+
subType?: string;
|
|
31
|
+
};
|
|
32
|
+
export type StoredImageMetadata = {
|
|
33
|
+
type: "image";
|
|
34
|
+
} & Omit<AddImageOptions, "id" | "url">;
|
|
35
|
+
export type StoredObjectDataTableMetadata = {
|
|
36
|
+
type: "dataTable";
|
|
37
|
+
} & Omit<AddDataTableOptions, "id" | "rows">;
|
|
38
|
+
export type StoredTextMetadata = {
|
|
39
|
+
type: "text";
|
|
40
|
+
} & Omit<AddTextOptions, "id" | "text">;
|
|
41
|
+
export type StoredObjectItemMetadata = {
|
|
42
|
+
type: "object";
|
|
43
|
+
keys: string[];
|
|
44
|
+
} & Omit<AddStoredObjectOptions, "id" | "data">;
|
|
45
|
+
export type StoredMetadataItem = StoredImageMetadata | StoredObjectDataTableMetadata | StoredTextMetadata | StoredObjectItemMetadata;
|
|
46
|
+
export type StoredMetadataItems = Record<string, StoredMetadataItem>;
|
|
47
|
+
export type StoredObjectType = "simulation-recording" | "untyped";
|
|
48
|
+
export type StoredObjectMetadata = {
|
|
49
|
+
version: 1;
|
|
50
|
+
type: StoredObjectType;
|
|
51
|
+
subType?: string;
|
|
52
|
+
items: StoredMetadataItems;
|
|
53
|
+
name?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
};
|
|
56
|
+
export type StoredObjectData = Record<string, any>;
|
|
57
|
+
export type StoredObjectOptions = {
|
|
58
|
+
id?: string;
|
|
59
|
+
name?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
type?: StoredObjectType;
|
|
62
|
+
subType?: string;
|
|
63
|
+
};
|
|
64
|
+
export declare class StoredObject {
|
|
65
|
+
id: string;
|
|
66
|
+
metadata: StoredObjectMetadata;
|
|
67
|
+
data: StoredObjectData;
|
|
68
|
+
constructor(options?: StoredObjectOptions);
|
|
69
|
+
static FromParts(id: string, metadata: StoredObjectMetadata, data: StoredObjectData): StoredObject;
|
|
70
|
+
addImage(options: AddImageOptions): void;
|
|
71
|
+
addDataTable(options: AddDataTableOptions): void;
|
|
72
|
+
addText(options: AddTextOptions): void;
|
|
73
|
+
addObject(options: AddStoredObjectOptions): void;
|
|
74
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StoredObject = void 0;
|
|
4
|
+
const nanoid_1 = require("nanoid");
|
|
5
|
+
class StoredObject {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.id = options?.id || (0, nanoid_1.nanoid)();
|
|
8
|
+
this.metadata = {
|
|
9
|
+
version: 1,
|
|
10
|
+
type: options?.type || "untyped",
|
|
11
|
+
items: {},
|
|
12
|
+
};
|
|
13
|
+
if (options?.name !== undefined) {
|
|
14
|
+
this.metadata.name = options.name;
|
|
15
|
+
}
|
|
16
|
+
if (options?.description !== undefined) {
|
|
17
|
+
this.metadata.description = options.description;
|
|
18
|
+
}
|
|
19
|
+
if (options?.subType !== undefined) {
|
|
20
|
+
this.metadata.subType = options.subType;
|
|
21
|
+
}
|
|
22
|
+
this.data = {};
|
|
23
|
+
}
|
|
24
|
+
static FromParts(id, metadata, data) {
|
|
25
|
+
const typedObject = new StoredObject({ id });
|
|
26
|
+
typedObject.metadata = metadata;
|
|
27
|
+
typedObject.data = data;
|
|
28
|
+
return typedObject;
|
|
29
|
+
}
|
|
30
|
+
addImage(options) {
|
|
31
|
+
const id = options.id || (0, nanoid_1.nanoid)();
|
|
32
|
+
this.metadata.items[id] = {
|
|
33
|
+
type: "image",
|
|
34
|
+
name: options.name
|
|
35
|
+
};
|
|
36
|
+
if (options.subType !== undefined) {
|
|
37
|
+
this.metadata.items[id].subType = options.subType;
|
|
38
|
+
}
|
|
39
|
+
if (options.width !== undefined) {
|
|
40
|
+
this.metadata.items[id].width = options.width;
|
|
41
|
+
}
|
|
42
|
+
if (options.height !== undefined) {
|
|
43
|
+
this.metadata.items[id].height = options.height;
|
|
44
|
+
}
|
|
45
|
+
if (options.description !== undefined) {
|
|
46
|
+
this.metadata.items[id].description = options.description;
|
|
47
|
+
}
|
|
48
|
+
this.data[id] = {
|
|
49
|
+
url: options.url
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
addDataTable(options) {
|
|
53
|
+
const id = options.id || (0, nanoid_1.nanoid)();
|
|
54
|
+
this.metadata.items[id] = {
|
|
55
|
+
type: "dataTable",
|
|
56
|
+
name: options.name,
|
|
57
|
+
cols: options.cols,
|
|
58
|
+
};
|
|
59
|
+
if (options.subType !== undefined) {
|
|
60
|
+
this.metadata.items[id].subType = options.subType;
|
|
61
|
+
}
|
|
62
|
+
if (options.description !== undefined) {
|
|
63
|
+
this.metadata.items[id].description = options.description;
|
|
64
|
+
}
|
|
65
|
+
this.data[id] = {
|
|
66
|
+
rows: options.rows
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
addText(options) {
|
|
70
|
+
const id = options.id || (0, nanoid_1.nanoid)();
|
|
71
|
+
this.metadata.items[id] = {
|
|
72
|
+
type: "text",
|
|
73
|
+
name: options.name,
|
|
74
|
+
};
|
|
75
|
+
if (options.subType !== undefined) {
|
|
76
|
+
this.metadata.items[id].subType = options.subType;
|
|
77
|
+
}
|
|
78
|
+
if (options.description !== undefined) {
|
|
79
|
+
this.metadata.items[id].description = options.description;
|
|
80
|
+
}
|
|
81
|
+
this.data[id] = {
|
|
82
|
+
text: options.text
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
addObject(options) {
|
|
86
|
+
const id = options.id || (0, nanoid_1.nanoid)();
|
|
87
|
+
this.metadata.items[id] = {
|
|
88
|
+
type: "object",
|
|
89
|
+
name: options.name,
|
|
90
|
+
keys: Object.keys(options.data),
|
|
91
|
+
};
|
|
92
|
+
if (options.subType !== undefined) {
|
|
93
|
+
this.metadata.items[id].subType = options.subType;
|
|
94
|
+
}
|
|
95
|
+
if (options.description !== undefined) {
|
|
96
|
+
this.metadata.items[id].description = options.description;
|
|
97
|
+
}
|
|
98
|
+
this.data[id] = options.data;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.StoredObject = StoredObject;
|
package/dist/typed-object.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export declare class TypedObject implements StoredObject {
|
|
|
64
64
|
data: TypedData;
|
|
65
65
|
constructor(options?: TypedObjectOptions);
|
|
66
66
|
static IsSupportedTypedObject(storedObject: StoredObject): boolean;
|
|
67
|
-
static IsSupportedTypedObjectMetadata(storedObjectMetadata?: StoredObject["metadata"]):
|
|
67
|
+
static IsSupportedTypedObjectMetadata(storedObjectMetadata?: StoredObject["metadata"]): storedObjectMetadata is TypedMetadata;
|
|
68
68
|
static FromStoredObject(id: string, storedObject: StoredObject): TypedObject;
|
|
69
69
|
addImage(options: AddImageOptions): void;
|
|
70
70
|
addDataTable(options: AddDataTableOptions): void;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { StoredObject, StoredObjectMetadata, StoredObjectData } from './stored-object';
|
|
1
2
|
export interface DemoObjectStorageConfig {
|
|
2
3
|
version: 1;
|
|
3
4
|
type: "demo";
|
|
@@ -7,8 +8,8 @@ export interface AuthenticatedUser {
|
|
|
7
8
|
jwt: string;
|
|
8
9
|
contextId: string;
|
|
9
10
|
platformId: string;
|
|
10
|
-
platformUserId: string;
|
|
11
11
|
resourceLinkId: string;
|
|
12
|
+
platformUserId: string;
|
|
12
13
|
}
|
|
13
14
|
export interface AnonymousUser {
|
|
14
15
|
type: "anonymous";
|
|
@@ -18,39 +19,33 @@ export type FirebaseObjectStorageUser = AuthenticatedUser | AnonymousUser;
|
|
|
18
19
|
export interface FirebaseObjectStorageConfig {
|
|
19
20
|
version: 1;
|
|
20
21
|
type: "firebase";
|
|
21
|
-
app:
|
|
22
|
+
app: Object;
|
|
22
23
|
root: string;
|
|
23
24
|
user: FirebaseObjectStorageUser;
|
|
25
|
+
questionId: string;
|
|
24
26
|
}
|
|
25
27
|
export type ObjectStorageConfig = DemoObjectStorageConfig | FirebaseObjectStorageConfig;
|
|
26
|
-
export interface AddOptions {
|
|
27
|
-
id?: string;
|
|
28
|
-
}
|
|
29
28
|
export interface IObjectStorage {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
monitorMine(callback: MonitorCallback): DemonitorFunction;
|
|
34
|
-
monitorLinked(callback: MonitorCallback): DemonitorFunction;
|
|
35
|
-
monitor(questionIds: string[], callback: MonitorCallback): DemonitorFunction;
|
|
36
|
-
add(object: StoredObject, options?: AddOptions): Promise<string>;
|
|
29
|
+
list(questionOrRefId: string): Promise<StoredObjectMetadataWithId[]>;
|
|
30
|
+
monitor(questionOrRefId: string, callback: MonitorCallback): DemonitorFunction;
|
|
31
|
+
add(object: StoredObject): Promise<StoredObject>;
|
|
37
32
|
read(objectId: string): Promise<StoredObject | undefined>;
|
|
38
|
-
readMetadata(objectId: string): Promise<
|
|
39
|
-
readData(objectId: string): Promise<
|
|
40
|
-
|
|
33
|
+
readMetadata(objectId: string): Promise<StoredObjectMetadata | undefined>;
|
|
34
|
+
readData(objectId: string): Promise<StoredObjectData | undefined>;
|
|
35
|
+
readDataItem(objectId: string, itemId: string): Promise<any | undefined>;
|
|
41
36
|
}
|
|
42
|
-
export interface
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export interface ObjectData {
|
|
46
|
-
[key: string]: any;
|
|
37
|
+
export interface StoredObjectMetadataWithId {
|
|
38
|
+
id: string;
|
|
39
|
+
metadata: StoredObjectMetadata;
|
|
47
40
|
}
|
|
48
|
-
export interface
|
|
49
|
-
|
|
50
|
-
data:
|
|
41
|
+
export interface StoredObjectDataWithId {
|
|
42
|
+
id: string;
|
|
43
|
+
data: StoredObjectData;
|
|
51
44
|
}
|
|
52
|
-
export interface
|
|
45
|
+
export interface StoredObjectWithId {
|
|
53
46
|
id: string;
|
|
47
|
+
metadata: StoredObjectMetadata;
|
|
48
|
+
data: StoredObjectData;
|
|
54
49
|
}
|
|
55
|
-
export type MonitorCallback = (objects:
|
|
50
|
+
export type MonitorCallback = (objects: StoredObjectMetadataWithId[]) => void;
|
|
56
51
|
export type DemonitorFunction = () => void;
|