@concord-consortium/object-storage 1.0.0-pre.4 → 1.0.0-pre.5

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.
@@ -1,7 +1,17 @@
1
+ import "firebase/compat/auth";
2
+ import "firebase/compat/firestore";
1
3
  import { FirebaseObjectStorageConfig, IObjectStorage, ObjectMetadata, ObjectData, StoredObject, ObjectWithId, MonitorCallback, DemonitorFunction, AddOptions } from './types';
2
4
  export declare class FirebaseObjectStorage implements IObjectStorage {
3
5
  private config;
6
+ private initPromise;
7
+ private initialized;
8
+ private app;
4
9
  constructor(config: FirebaseObjectStorageConfig);
10
+ private initialize;
11
+ private ensureInitialized;
12
+ private createDocument;
13
+ private getPaths;
14
+ private getRefs;
5
15
  /**
6
16
  * Lists metadata documents for objects owned by the current user
7
17
  */
@@ -1,18 +1,79 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.FirebaseObjectStorage = void 0;
4
7
  const nanoid_1 = require("nanoid");
8
+ const app_1 = __importDefault(require("firebase/compat/app"));
9
+ require("firebase/compat/auth");
10
+ require("firebase/compat/firestore");
5
11
  class FirebaseObjectStorage {
6
12
  constructor(config) {
13
+ this.initialized = false;
7
14
  if (config.version !== 1) {
8
15
  throw new Error(`Unsupported config version: ${config.version}. Expected version 1.`);
9
16
  }
10
17
  this.config = config;
18
+ this.app = app_1.default.initializeApp(this.config.app);
19
+ this.app.firestore().settings({
20
+ ignoreUndefinedProperties: true,
21
+ });
22
+ // Start initialization immediately in constructor
23
+ this.initPromise = this.initialize();
24
+ }
25
+ async initialize() {
26
+ if (this.initialized)
27
+ return;
28
+ if (this.config.user.type === "authenticated") {
29
+ // ensure any previous auth state is cleared before signing in
30
+ await this.app.auth().signOut();
31
+ await this.app.auth().signInWithCustomToken(this.config.user.jwt);
32
+ }
33
+ this.initialized = true;
34
+ }
35
+ async ensureInitialized() {
36
+ await this.initPromise;
37
+ }
38
+ createDocument(contents) {
39
+ if (this.config.user.type === "authenticated") {
40
+ const { contextId, platformId, platformUserId, resourceLinkId } = this.config.user;
41
+ return {
42
+ created_at: app_1.default.firestore.FieldValue.serverTimestamp(),
43
+ platform_id: platformId,
44
+ platform_user_id: platformUserId.toString(),
45
+ context_id: contextId,
46
+ resource_link_id: resourceLinkId,
47
+ run_key: "",
48
+ ...contents
49
+ };
50
+ }
51
+ else {
52
+ const { runKey } = this.config.user;
53
+ return {
54
+ created_at: app_1.default.firestore.FieldValue.serverTimestamp(),
55
+ run_key: runKey,
56
+ platform_user_id: runKey,
57
+ ...contents
58
+ };
59
+ }
60
+ }
61
+ getPaths(objectId) {
62
+ const metadataPath = `${this.config.root}/object_store_metadata/${objectId}`;
63
+ const dataPath = `${this.config.root}/object_store_data/${objectId}`;
64
+ return { metadataPath, dataPath };
65
+ }
66
+ getRefs(objectId) {
67
+ const { metadataPath, dataPath } = this.getPaths(objectId);
68
+ const metadataRef = this.app.firestore().doc(metadataPath);
69
+ const dataRef = this.app.firestore().doc(dataPath);
70
+ return { metadataRef, dataRef };
11
71
  }
12
72
  /**
13
73
  * Lists metadata documents for objects owned by the current user
14
74
  */
15
75
  async listMine() {
76
+ await this.ensureInitialized();
16
77
  // TODO: Implement Firebase query for user's own objects
17
78
  return [];
18
79
  }
@@ -20,6 +81,7 @@ class FirebaseObjectStorage {
20
81
  * Lists metadata documents for objects linked to the current user
21
82
  */
22
83
  async listLinked() {
84
+ await this.ensureInitialized();
23
85
  // TODO: Implement Firebase query for linked objects
24
86
  return [];
25
87
  }
@@ -27,6 +89,7 @@ class FirebaseObjectStorage {
27
89
  * Lists metadata documents for objects associated with specific question IDs
28
90
  */
29
91
  async list(questionIds) {
92
+ await this.ensureInitialized();
30
93
  // TODO: Implement Firebase query for objects by question IDs
31
94
  return [];
32
95
  }
@@ -36,6 +99,7 @@ class FirebaseObjectStorage {
36
99
  * Returns a function to stop monitoring
37
100
  */
38
101
  monitorMine(callback) {
102
+ // await this.ensureInitialized();
39
103
  // TODO: Implement Firebase realtime listener for user's own objects
40
104
  callback([]);
41
105
  return () => {
@@ -48,6 +112,7 @@ class FirebaseObjectStorage {
48
112
  * Returns a function to stop monitoring
49
113
  */
50
114
  monitorLinked(callback) {
115
+ // await this.ensureInitialized();
51
116
  // TODO: Implement Firebase realtime listener for linked objects
52
117
  callback([]);
53
118
  return () => {
@@ -60,6 +125,7 @@ class FirebaseObjectStorage {
60
125
  * Returns a function to stop monitoring
61
126
  */
62
127
  monitor(questionIds, callback) {
128
+ // await this.ensureInitialized();
63
129
  // TODO: Implement Firebase realtime listener for objects by question IDs
64
130
  callback([]);
65
131
  return () => {
@@ -71,16 +137,23 @@ class FirebaseObjectStorage {
71
137
  * Returns the generated object ID (nanoid) or the provided ID if specified in options
72
138
  */
73
139
  async add(object, options) {
74
- // TODO: Generate nanoid if not provided in options
75
- // TODO: Add metadata document to Firebase
76
- // TODO: Add data document to Firebase
77
- const newObjectId = options?.id ?? 'placeholder-id';
140
+ await this.ensureInitialized();
141
+ const newObjectId = options?.id ?? (0, nanoid_1.nanoid)();
142
+ const { data, metadata } = object;
143
+ const { metadataRef, dataRef } = this.getRefs(newObjectId);
144
+ const dataDoc = this.createDocument({ data });
145
+ const metadataDoc = this.createDocument({ metadata });
146
+ const batch = this.app.firestore().batch();
147
+ batch.set(dataRef, dataDoc);
148
+ batch.set(metadataRef, metadataDoc);
149
+ await batch.commit();
78
150
  return newObjectId;
79
151
  }
80
152
  /**
81
153
  * Reads both metadata and data documents for an object
82
154
  */
83
155
  async read(objectId) {
156
+ await this.ensureInitialized();
84
157
  // TODO: Read metadata document from Firebase
85
158
  // TODO: Read data document from Firebase
86
159
  return {
@@ -92,6 +165,7 @@ class FirebaseObjectStorage {
92
165
  * Reads only the metadata document for an object
93
166
  */
94
167
  async readMetadata(objectId) {
168
+ await this.ensureInitialized();
95
169
  // TODO: Read metadata document from Firebase
96
170
  return {};
97
171
  }
@@ -99,6 +173,7 @@ class FirebaseObjectStorage {
99
173
  * Reads only the data document for an object
100
174
  */
101
175
  async readData(objectId) {
176
+ await this.ensureInitialized();
102
177
  // TODO: Read data document from Firebase
103
178
  return {};
104
179
  }
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, FirebaseObjectStorageConfig, ObjectMetadata, ObjectData, StoredObject, ObjectWithId, MonitorCallback, DemonitorFunction } from './types';
5
+ export { IObjectStorage, ObjectStorageConfig, DemoObjectStorageConfig, FirebaseObjectStorageUser, FirebaseObjectStorageConfig, ObjectMetadata, ObjectData, StoredObject, ObjectWithId, MonitorCallback, DemonitorFunction } from './types';
6
6
  export * from './typed-object';
@@ -69,8 +69,13 @@ class TypedObject {
69
69
  if (options.description !== undefined) {
70
70
  this.metadata.items[id].description = options.description;
71
71
  }
72
+ // firebase does not support nested arrays well, so we store rows as an object with numeric keys
73
+ const rowsObj = {};
74
+ options.rows.forEach((row, index) => {
75
+ rowsObj[index.toString()] = row;
76
+ });
72
77
  this.data[id] = {
73
- rows: options.rows
78
+ rows: rowsObj
74
79
  };
75
80
  }
76
81
  addText(options) {
package/dist/types.d.ts CHANGED
@@ -1,10 +1,26 @@
1
1
  export interface DemoObjectStorageConfig {
2
- type: "demo";
3
2
  version: 1;
3
+ type: "demo";
4
+ }
5
+ export interface AuthenticatedUser {
6
+ type: "authenticated";
7
+ jwt: string;
8
+ contextId: string;
9
+ platformId: string;
10
+ platformUserId: string;
11
+ resourceLinkId: string;
4
12
  }
13
+ export interface AnonymousUser {
14
+ type: "anonymous";
15
+ runKey: string;
16
+ }
17
+ export type FirebaseObjectStorageUser = AuthenticatedUser | AnonymousUser;
5
18
  export interface FirebaseObjectStorageConfig {
6
- type: "firebase";
7
19
  version: 1;
20
+ type: "firebase";
21
+ app: any;
22
+ root: string;
23
+ user: FirebaseObjectStorageUser;
8
24
  }
9
25
  export type ObjectStorageConfig = DemoObjectStorageConfig | FirebaseObjectStorageConfig;
10
26
  export interface AddOptions {
package/dist/types.js CHANGED
@@ -1,2 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ ;
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@concord-consortium/object-storage",
3
- "version": "1.0.0-pre.4",
3
+ "version": "1.0.0-pre.5",
4
4
  "description": "A TypeScript library for object storage",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
+ "build:watch": "tsc --watch",
9
10
  "test": "jest",
10
11
  "test:watch": "jest --watch",
11
12
  "test:coverage": "jest --coverage",
@@ -25,6 +26,7 @@
25
26
  "access": "public"
26
27
  },
27
28
  "dependencies": {
29
+ "firebase": "^9.8.1",
28
30
  "nanoid": "^3.3.7"
29
31
  },
30
32
  "peerDependencies": {