@nu-art/thunderstorm-backend 0.400.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.
Files changed (111) hide show
  1. package/_entity/app-config/ModuleBE_AppConfigAPI.d.ts +9 -0
  2. package/_entity/app-config/ModuleBE_AppConfigAPI.js +20 -0
  3. package/_entity/app-config/ModuleBE_AppConfigDB.d.ts +27 -0
  4. package/_entity/app-config/ModuleBE_AppConfigDB.js +91 -0
  5. package/_entity/app-config/index.d.ts +2 -0
  6. package/_entity/app-config/index.js +2 -0
  7. package/_entity/app-config/module-pack.d.ts +2 -0
  8. package/_entity/app-config/module-pack.js +3 -0
  9. package/_entity/backup-doc/ModuleBE_BackupDocDB.d.ts +52 -0
  10. package/_entity/backup-doc/ModuleBE_BackupDocDB.js +350 -0
  11. package/_entity/backup-doc/ModuleBE_BackupScheduler.d.ts +7 -0
  12. package/_entity/backup-doc/ModuleBE_BackupScheduler.js +14 -0
  13. package/_entity/backup-doc/index.d.ts +3 -0
  14. package/_entity/backup-doc/index.js +3 -0
  15. package/_entity/backup-doc/module-pack.d.ts +2 -0
  16. package/_entity/backup-doc/module-pack.js +3 -0
  17. package/_entity/editable-test/ModuleBE_EditableTestDB.d.ts +8 -0
  18. package/_entity/editable-test/ModuleBE_EditableTestDB.js +8 -0
  19. package/_entity/editable-test/index.d.ts +1 -0
  20. package/_entity/editable-test/index.js +1 -0
  21. package/_entity/editable-test/module-pack.d.ts +1 -0
  22. package/_entity/editable-test/module-pack.js +3 -0
  23. package/_entity.d.ts +3 -0
  24. package/_entity.js +3 -0
  25. package/core/BaseStorm.d.ts +17 -0
  26. package/core/BaseStorm.js +77 -0
  27. package/core/Storm.d.ts +15 -0
  28. package/core/Storm.js +93 -0
  29. package/core/db-def.d.ts +10 -0
  30. package/core/db-def.js +11 -0
  31. package/core/default-storm.d.ts +3 -0
  32. package/core/default-storm.js +30 -0
  33. package/core/storm-modulepack.d.ts +3 -0
  34. package/core/storm-modulepack.js +20 -0
  35. package/core/typed-api.d.ts +7 -0
  36. package/core/typed-api.js +46 -0
  37. package/exceptions.d.ts +1 -0
  38. package/exceptions.js +21 -0
  39. package/index.d.ts +27 -0
  40. package/index.js +48 -0
  41. package/modules/CleanupScheduler.d.ts +14 -0
  42. package/modules/CleanupScheduler.js +50 -0
  43. package/modules/ModuleBE_APIs.d.ts +11 -0
  44. package/modules/ModuleBE_APIs.js +19 -0
  45. package/modules/ModuleBE_CSVParser.d.ts +9 -0
  46. package/modules/ModuleBE_CSVParser.js +50 -0
  47. package/modules/ModuleBE_ForceUpgrade.d.ts +21 -0
  48. package/modules/ModuleBE_ForceUpgrade.js +70 -0
  49. package/modules/ModuleBE_ServerInfo.d.ts +20 -0
  50. package/modules/ModuleBE_ServerInfo.js +76 -0
  51. package/modules/_imports.d.ts +6 -0
  52. package/modules/_imports.js +26 -0
  53. package/modules/_tdb/service-accounts.d.ts +19 -0
  54. package/modules/_tdb/service-accounts.js +2 -0
  55. package/modules/action-processor/Action_SetupProject.d.ts +9 -0
  56. package/modules/action-processor/Action_SetupProject.js +23 -0
  57. package/modules/action-processor/ModuleBE_ActionProcessor.d.ts +11 -0
  58. package/modules/action-processor/ModuleBE_ActionProcessor.js +67 -0
  59. package/modules/action-processor/types.d.ts +10 -0
  60. package/modules/action-processor/types.js +1 -0
  61. package/modules/archiving/ModuleBE_Archiving.d.ts +119 -0
  62. package/modules/archiving/ModuleBE_Archiving.js +236 -0
  63. package/modules/collection-actions/ModuleBE_CollectionActions.d.ts +12 -0
  64. package/modules/collection-actions/ModuleBE_CollectionActions.js +69 -0
  65. package/modules/collection-actions/dispatcher.d.ts +7 -0
  66. package/modules/collection-actions/dispatcher.js +2 -0
  67. package/modules/db-api-gen/ModuleBE_BaseApi.d.ts +16 -0
  68. package/modules/db-api-gen/ModuleBE_BaseApi.js +74 -0
  69. package/modules/db-api-gen/ModuleBE_BaseDB.d.ts +78 -0
  70. package/modules/db-api-gen/ModuleBE_BaseDB.js +298 -0
  71. package/modules/http/AxiosHttpModule.d.ts +25 -0
  72. package/modules/http/AxiosHttpModule.js +132 -0
  73. package/modules/http/types.d.ts +6 -0
  74. package/modules/http/types.js +1 -0
  75. package/modules/proxy/ModuleBE_RemoteProxy.d.ts +35 -0
  76. package/modules/proxy/ModuleBE_RemoteProxy.js +86 -0
  77. package/modules/proxy/RemoteProxyCaller.d.ts +19 -0
  78. package/modules/proxy/RemoteProxyCaller.js +82 -0
  79. package/modules/proxy/assert-secret-middleware.d.ts +2 -0
  80. package/modules/proxy/assert-secret-middleware.js +24 -0
  81. package/modules/server/HeaderKey.d.ts +8 -0
  82. package/modules/server/HeaderKey.js +41 -0
  83. package/modules/server/HttpServer.d.ts +41 -0
  84. package/modules/server/HttpServer.js +223 -0
  85. package/modules/server/consts.d.ts +13 -0
  86. package/modules/server/consts.js +9 -0
  87. package/modules/server/route-resolvers/RouteResolver_Dummy.d.ts +7 -0
  88. package/modules/server/route-resolvers/RouteResolver_Dummy.js +34 -0
  89. package/modules/server/route-resolvers/RouteResolver_ModulePath.d.ts +22 -0
  90. package/modules/server/route-resolvers/RouteResolver_ModulePath.js +84 -0
  91. package/modules/server/route-resolvers/index.d.ts +7 -0
  92. package/modules/server/route-resolvers/index.js +21 -0
  93. package/modules/server/server-api.d.ts +85 -0
  94. package/modules/server/server-api.js +362 -0
  95. package/modules/server/server-errors.d.ts +4 -0
  96. package/modules/server/server-errors.js +79 -0
  97. package/modules/sync-env/ModuleBE_SyncEnv.d.ts +36 -0
  98. package/modules/sync-env/ModuleBE_SyncEnv.js +212 -0
  99. package/modules/sync-manager/ModuleBE_SyncManager.d.ts +63 -0
  100. package/modules/sync-manager/ModuleBE_SyncManager.js +254 -0
  101. package/package.json +104 -0
  102. package/shared.d.ts +1 -0
  103. package/shared.js +21 -0
  104. package/test/StormTest.d.ts +23 -0
  105. package/test/StormTest.js +49 -0
  106. package/utils/file.d.ts +2 -0
  107. package/utils/file.js +29 -0
  108. package/utils/promisify-request.d.ts +3 -0
  109. package/utils/promisify-request.js +33 -0
  110. package/utils/types.d.ts +11 -0
  111. package/utils/types.js +21 -0
@@ -0,0 +1,236 @@
1
+ import { BadImplementationException, batchActionParallel, currentTimeMillis, Day, dbIdLength, deepClone, generateHex, Hour, removeDBObjectKeys, RuntimeModules } from '@nu-art/ts-common';
2
+ import { ModuleBE_FirestoreListener } from '@nu-art/firebase-backend';
3
+ import { addRoutes } from '../ModuleBE_APIs.js';
4
+ import { createBodyServerApi, createQueryServerApi } from '../../core/typed-api.js';
5
+ import { ApiDef_Archiving } from '@nu-art/thunderstorm-shared';
6
+ import { _EmptyQuery } from '@nu-art/firebase-shared';
7
+ export const Const_ArchivedCollectionPath = '/_archived';
8
+ /**
9
+ * This class extends FirestoreFunctionModule and handles Firestore database operations
10
+ * with custom logic for archiving and Time-To-Live (TTL) functionality.
11
+ */
12
+ export class ModuleBE_ArchiveModule_Class extends ModuleBE_FirestoreListener {
13
+ TTL; // Time to live for each instance
14
+ lastUpdatedTTL; // Time to live after last update
15
+ moduleMapper; // Module mapper, mapping collection name to module
16
+ /**
17
+ * Constructor initializes TTL, lastUpdatedTTL moduleMapper and sets api routes for the module.
18
+ */
19
+ constructor() {
20
+ super('{collectionName}');
21
+ this.moduleMapper = {};
22
+ this.lastUpdatedTTL = Day; // Default TTL for last updated is one day
23
+ this.TTL = Hour * 2; // Default TTL is two hours
24
+ }
25
+ /**
26
+ * Initializes the `moduleMapper` by populating it with Firestore collections.
27
+ * Iterates through all modules obtained from the Storm instance and adds modules
28
+ * which are Firestore DB modules to the `moduleMapper`.
29
+ */
30
+ init() {
31
+ super.init();
32
+ // Add all DB modules to the mapper
33
+ RuntimeModules()
34
+ .filter((module) => !!module.dbDef)
35
+ .forEach(_module => this.moduleMapper[_module.collection.collection.path] = _module);
36
+ addRoutes([
37
+ createBodyServerApi(ApiDef_Archiving.vv1.hardDeleteUnique, this.hardDeleteUnique),
38
+ createQueryServerApi(ApiDef_Archiving.vv1.hardDeleteAll, this.hardDeleteAll),
39
+ createQueryServerApi(ApiDef_Archiving.vv1.getDocumentHistory, this.getDocumentHistory)
40
+ ]);
41
+ }
42
+ /**
43
+ * Deletes a unique document by its ID in a Firestore transaction.
44
+ * This method first retrieves the document with the given ID.
45
+ * If the document is found, it is marked for deletion and an upsert operation is performed.
46
+ * The upsert operation triggers a Firestore OnUpdate event, which will delete the document and its '_archived' sub-collection.
47
+ *
48
+ * @param body - An object of type `RequestBody_HardDeleteUnique` containing the following fields:
49
+ * - _id: The ID of the document to be deleted.
50
+ * - collectionName: The name of the collection the document belongs to.
51
+ * - dbInstance: (optional) The instance of the document. If not provided, the method will attempt to retrieve it using the given ID.
52
+ * @returns - A promise that performs the deletion operation.
53
+ * @throws - A `BadImplementationException` if no module is found for the given collection or if no document with the provided ID is found.
54
+ */
55
+ hardDeleteUnique = async (body) => {
56
+ const { _id, collectionName, dbInstance } = body;
57
+ const dbModule = this.moduleMapper[collectionName];
58
+ if (!dbModule)
59
+ throw new BadImplementationException('no module found');
60
+ return dbModule.runTransaction(async (transaction) => {
61
+ const instance = dbInstance ?? await dbModule.query.unique(_id, transaction);
62
+ // queryUnique(dbModule.collection, {where: {_id} as Clause_Where<DBType>});
63
+ if (!instance)
64
+ throw new BadImplementationException(`couldn't find doc with id ${_id}`);
65
+ //make sure trigger will delete object, and it's _archived collection
66
+ instance.__hardDelete = true;
67
+ await dbModule.set.item(instance, transaction);
68
+ });
69
+ };
70
+ /**
71
+ * Deletes all documents in the specified collection.
72
+ * This function first retrieves all documents in the collection.
73
+ * It then deletes each document in the collection in parallel chunks for efficiency.
74
+ *
75
+ * @param queryParams - Params includes the name of the collection in which the documents will be deleted.
76
+ * @returns - A promise that performs the deletion operation.
77
+ * @throws - A BadImplementationException if no corresponding module is found for the given collection.
78
+ */
79
+ hardDeleteAll = async (queryParams) => {
80
+ const dbModule = this.moduleMapper[queryParams.collectionName];
81
+ if (!dbModule)
82
+ throw new BadImplementationException('no module found');
83
+ const collectionItems = await dbModule.query.custom(_EmptyQuery);
84
+ await batchActionParallel(collectionItems, 10, (chunk) => Promise.all(chunk.map(item => this.hardDeleteUnique({
85
+ _id: item._id,
86
+ collectionName: dbModule.collection.collection.path,
87
+ dbInstance: item
88
+ }))));
89
+ };
90
+ /**
91
+ * Asynchronously retrieves the document history from the '_archived' collection group.
92
+ *
93
+ * This function takes a RequestQuery_GetHistory object as a parameter, which contains the name of the collection
94
+ * and the ID of the document for which the history should be retrieved.
95
+ * It then finds the respective Firestore DB module for the provided collection.
96
+ *
97
+ * If no module is found for the given collection, a BadImplementationException is thrown.
98
+ *
99
+ * The function queries the '_archived' collection group where the '_originDocId' field matches the provided
100
+ * document ID, and orders the results by the '__created' timestamp in descending order.
101
+ * It then maps the document snapshots to their respective data, creating an array of DBType documents, which is returned.
102
+ *
103
+ * @param queryParams - The request query parameters containing the collection name and the document ID.
104
+ * @returns - An array of DBType documents representing the history of the specified document.
105
+ * @throws - A BadImplementationException if no module is found for the given collection.
106
+ */
107
+ getDocumentHistory = async (queryParams) => {
108
+ const { collectionName, _id } = queryParams;
109
+ const dbModule = this.moduleMapper[collectionName];
110
+ if (!dbModule)
111
+ throw new BadImplementationException('no db module found');
112
+ const collectionGroup = dbModule.collection.collection.firestore.collectionGroup('_archived');
113
+ const query = collectionGroup.where('_originDocId', '==', _id).orderBy('__created', 'desc');
114
+ const snapshot = await query.get();
115
+ const docs = snapshot.docs.map(doc => doc.data());
116
+ return docs.filter((doc) => !doc.__collectionName);
117
+ };
118
+ /**
119
+ * Checks if the Time-To-Live (TTL) for a document instance has been exceeded.
120
+ *
121
+ * @param instance - The document instance to check.
122
+ * @param dbModule - The Firestore database module the document belongs to.
123
+ * @returns - A boolean indicating whether the TTL has been exceeded (true) or not (false).
124
+ */
125
+ checkTTL(instance, dbModule) {
126
+ const timestamp = currentTimeMillis();
127
+ const TTL = dbModule.dbDef.TTL || this.TTL;
128
+ // If TTL is not set or the document is not updated, return false
129
+ if (TTL === -1 || !instance.__updated)
130
+ return false;
131
+ // Check if the current time is past the document's TTL
132
+ return timestamp > (instance.__updated + TTL);
133
+ }
134
+ /**
135
+ * Checks if the `lastUpdatedTTL` for a document instance has been exceeded.
136
+ * This represents a secondary TTL which is based on the last update time of the document.
137
+ *
138
+ * @param instance - The document instance to check.
139
+ * @param dbModule - The Firestore database module the document belongs to.
140
+ * @returns - A boolean indicating whether the `lastUpdatedTTL` has been exceeded (true) or not (false).
141
+ */
142
+ checkLastUpdatedTTL(instance, dbModule) {
143
+ const timestamp = currentTimeMillis();
144
+ const lastUpdatedTTL = dbModule.dbDef.lastUpdatedTTL || this.lastUpdatedTTL;
145
+ // If lastUpdatedTTL is not set or the document is not updated, return false
146
+ if (lastUpdatedTTL === -1 || !instance.__updated)
147
+ return false;
148
+ // Check if the current time is past the document's lastUpdatedTTL
149
+ return timestamp > (instance.__updated + lastUpdatedTTL);
150
+ }
151
+ /**
152
+ * Inserts a document into the '_archived' sub-collection.
153
+ * This function is used for archiving the previous state of the document before it was changed.
154
+ *
155
+ * @param dbModule - The Firestore database module the document belongs to.
156
+ * @param before - The state of the document before changes.
157
+ * @returns - A promise that performs the archiving operation or undefined in case of an error.
158
+ */
159
+ async insertToArchive(dbModule, before) {
160
+ if (before.__hardDelete)
161
+ return;
162
+ // Reference to the original collection
163
+ const collectionRef = dbModule.collection.collection;
164
+ const timestamp = currentTimeMillis();
165
+ // Deep clone the document before mutation
166
+ let dbInstance = deepClone(before);
167
+ // Reference to the _archived sub-collection
168
+ const subCollection = collectionRef.doc(dbInstance._id).collection(Const_ArchivedCollectionPath);
169
+ // Remove the keys from the original object that shouldn't be in the archive
170
+ dbInstance = removeDBObjectKeys(dbInstance);
171
+ // Record the original document ID
172
+ dbInstance._originDocId = before._id;
173
+ // Generate a new ID for the archived document
174
+ dbInstance._id = generateHex(dbIdLength);
175
+ dbInstance.__updated = timestamp;
176
+ dbInstance.__created = timestamp;
177
+ // Insert the archived document into the _archived sub-collection
178
+ await subCollection.doc(dbInstance._id).set(dbInstance);
179
+ }
180
+ /**
181
+ * Hard deletes a document and its associated archived documents.
182
+ *
183
+ * @param instance - The instance of the document to delete.
184
+ * @param dbModule - The Firestore database module the document belongs to.
185
+ * @returns - A promise to perform the deletion operation.
186
+ */
187
+ async hardDeleteDoc(instance, dbModule) {
188
+ // Get reference to the collection the document belongs to
189
+ const collectionRef = dbModule.collection.collection;
190
+ // Get reference to the document instance to delete
191
+ const instanceRef = collectionRef.doc(instance._id);
192
+ // Get reference to the archived documents collection associated with the document instance
193
+ const archivedCollectionRef = instanceRef.collection(Const_ArchivedCollectionPath);
194
+ // Get all archived documents
195
+ const archivedDocs = await archivedCollectionRef.listDocuments();
196
+ // Delete the document instance
197
+ await instanceRef.delete();
198
+ // Delete all archived documents associated with the document instance, performing the delete operation in chunks of 10
199
+ return batchActionParallel(archivedDocs, 10, (docChunk) => Promise.all(docChunk.map(async (doc) => {
200
+ await doc.set({ __hardDelete: true }, { merge: true });
201
+ return doc.delete();
202
+ })));
203
+ }
204
+ /**
205
+ * Processes changes in the Firestore collection.
206
+ * Depending on the state of the documents, it either archives the documents,
207
+ * checks the Time-To-Live (TTL) or performs a hard delete operation.
208
+ *
209
+ * @param params - An object containing the collectionName and the document ID.
210
+ * @param before - The state of the document before changes.
211
+ * @param after - The state of the document after changes.
212
+ * @returns - A promise that performs the necessary operation based on the document states.
213
+ */
214
+ async processChanges(params, before, after) {
215
+ // Get the relevant module
216
+ const dbModule = this.moduleMapper[params.collectionName];
217
+ if (!dbModule)
218
+ throw new BadImplementationException('no db module found');
219
+ // If there's no previous document state, or it's marked for hard deletion, exit the function
220
+ if (!before)
221
+ return;
222
+ // If the document was deleted, archive the original document
223
+ if (!after)
224
+ return this.insertToArchive(dbModule, before);
225
+ // If the document is marked for hard deletion, delete the document
226
+ if (after.__hardDelete)
227
+ return this.hardDeleteDoc(before, dbModule);
228
+ // If the document's TTL has been exceeded, archive the original document
229
+ if (this.checkTTL(before, dbModule))
230
+ return this.insertToArchive(dbModule, before);
231
+ // If the document's lastUpdatedTTL has been exceeded, archive the original document
232
+ if (this.checkLastUpdatedTTL(before, dbModule))
233
+ return this.insertToArchive(dbModule, before);
234
+ }
235
+ }
236
+ export const ModuleBE_Archiving = new ModuleBE_ArchiveModule_Class();
@@ -0,0 +1,12 @@
1
+ import { Module } from '@nu-art/ts-common';
2
+ import { CollectionActions_Check, CollectionActions_Upgrade } from '../../shared.js';
3
+ declare class ModuleBE_CollectionActions_Class extends Module {
4
+ constructor();
5
+ protected init(): void;
6
+ private getUpgradableModules;
7
+ upgrade_Collections: (req: CollectionActions_Upgrade["collections"]["request"]) => Promise<CollectionActions_Upgrade["collections"]["response"]>;
8
+ upgrade_All: (req: CollectionActions_Upgrade["all"]["request"]) => Promise<CollectionActions_Upgrade["all"]["response"]>;
9
+ check_Usage: (req: CollectionActions_Check["usage"]["request"]) => Promise<CollectionActions_Check["usage"]["response"]>;
10
+ }
11
+ export declare const ModuleBE_CollectionActions: ModuleBE_CollectionActions_Class;
12
+ export {};
@@ -0,0 +1,69 @@
1
+ import { _keys, filterInstances, LogLevel, merge, Module, Promise_all_sequentially, RuntimeModules } from '@nu-art/ts-common';
2
+ import { addRoutes } from '../ModuleBE_APIs.js';
3
+ import { createBodyServerApi } from '../../core/typed-api.js';
4
+ import { ApiDef_CollectionActions } from '../../shared.js';
5
+ import { dispatch_CollectEntityDependencies } from './dispatcher.js';
6
+ class ModuleBE_CollectionActions_Class extends Module {
7
+ constructor() {
8
+ super();
9
+ this.setMinLevel(LogLevel.Verbose);
10
+ }
11
+ init() {
12
+ super.init();
13
+ addRoutes([
14
+ //Add upgrade routes
15
+ createBodyServerApi(ApiDef_CollectionActions.upgrade.collections, this.upgrade_Collections),
16
+ createBodyServerApi(ApiDef_CollectionActions.upgrade.all, this.upgrade_All),
17
+ //Add check routes
18
+ createBodyServerApi(ApiDef_CollectionActions.check.usage, this.check_Usage),
19
+ ]);
20
+ }
21
+ // ##################### Internal Logic #####################
22
+ getUpgradableModules = async (limitKeys, forceUpgrade) => {
23
+ const filterModule = async (_module) => {
24
+ const module = _module;
25
+ const dbKey = module?.dbDef?.dbKey;
26
+ //Assert dbKey exists
27
+ if (!dbKey)
28
+ return undefined;
29
+ //Assert not an advisor collection module
30
+ if (module.advisorCollectionModule)
31
+ return undefined;
32
+ //Assert in the user given keys
33
+ if (limitKeys.length > 0 && !limitKeys.includes(dbKey))
34
+ return undefined;
35
+ const isUpToDate = await module.isCollectionUpToDate();
36
+ return (!isUpToDate || forceUpgrade) ? module : undefined;
37
+ };
38
+ const allModules = RuntimeModules().all;
39
+ return filterInstances(await Promise.all(allModules.map(filterModule)));
40
+ };
41
+ // ##################### API Callbacks - Upgrade #####################
42
+ upgrade_Collections = async (req) => {
43
+ this.logInfo(`Upgrade - Collections${req.force ? ', Forcefully' : ''}`);
44
+ if (req.dbKeys.length)
45
+ this.logInfo(`Limited to ${req.dbKeys.length} collections:`, req.dbKeys.join(', '));
46
+ else
47
+ this.logInfo('No collection limit');
48
+ const modules = await this.getUpgradableModules(req.dbKeys, req.force);
49
+ this.logInfo(`Will upgrade ${modules.length} modules`);
50
+ await Promise_all_sequentially(modules.map(module => () => module.upgradeCollection(req.force)));
51
+ };
52
+ upgrade_All = async (req) => {
53
+ this.logInfo('Upgrade - All');
54
+ //Call upgrade collections with no dbKey limit
55
+ await this.upgrade_Collections({ dbKeys: [], force: req.force });
56
+ };
57
+ // ##################### API Callbacks - Check #####################
58
+ check_Usage = async (req) => {
59
+ this.logInfo(`Checking usage for ${req.itemIds.length} items under the "${req.dbKey}" collection`);
60
+ const dependencies = await dispatch_CollectEntityDependencies.dispatchModuleAsync(req.dbKey, req.itemIds);
61
+ const filtered = filterInstances(dependencies);
62
+ if (!filtered.length)
63
+ return { dependencies: undefined };
64
+ const merged = filtered.reduce((acc, dependency) => merge(acc, dependency));
65
+ const dependenciesAmount = _keys(merged.dependencyMap).length;
66
+ return { dependencies: dependenciesAmount ? merged : undefined };
67
+ };
68
+ }
69
+ export const ModuleBE_CollectionActions = new ModuleBE_CollectionActions_Class();
@@ -0,0 +1,7 @@
1
+ import { DBProto, Dispatcher } from '@nu-art/ts-common';
2
+ import { Transaction } from 'firebase-admin/firestore';
3
+ import { DBEntityDependencies } from '../../shared.js';
4
+ export interface EntityDependencyCollection {
5
+ __collectEntityDependencies: <T extends DBProto<any>>(type: T['dbKey'], itemIds: string[], transaction?: Transaction) => Promise<DBEntityDependencies | undefined>;
6
+ }
7
+ export declare const dispatch_CollectEntityDependencies: Dispatcher<EntityDependencyCollection, "__collectEntityDependencies", [type: any, itemIds: string[], transaction?: Transaction | undefined], DBEntityDependencies | undefined>;
@@ -0,0 +1,2 @@
1
+ import { Dispatcher } from '@nu-art/ts-common';
2
+ export const dispatch_CollectEntityDependencies = new Dispatcher('__collectEntityDependencies');
@@ -0,0 +1,16 @@
1
+ import { DBProto, Module } from '@nu-art/ts-common';
2
+ import { ModuleBE_BaseDB } from './ModuleBE_BaseDB.js';
3
+ /**
4
+ * A base class used for implementing CRUD operations on a db module collection.
5
+ *
6
+ * By default, it exposes API endpoints for creating, deleting, updating, querying and querying for unique document.
7
+ */
8
+ export declare class ModuleBE_BaseApi_Class<Proto extends DBProto<any>> extends Module {
9
+ readonly dbModule: ModuleBE_BaseDB<Proto>;
10
+ readonly apiDef: import("@nu-art/thunderstorm-shared").ApiDefRouter<import("@nu-art/thunderstorm-shared").ApiStruct_DBApiGenIDBV3<Proto>>;
11
+ constructor(dbModule: ModuleBE_BaseDB<Proto, any>, version?: string);
12
+ init(): void;
13
+ private _metadata;
14
+ private _deleteQuery;
15
+ }
16
+ export declare const createApisForDBModuleV3: <Proto extends DBProto<any>>(dbModule: ModuleBE_BaseDB<Proto>, version?: string) => ModuleBE_BaseApi_Class<Proto>;
@@ -0,0 +1,74 @@
1
+ /*
2
+ * Database API Generator is a utility library for Thunderstorm.
3
+ *
4
+ * Given proper configurations it will dynamically generate APIs to your Firestore
5
+ * collections, will assert uniqueness and restrict deletion... and more
6
+ *
7
+ * Copyright (C) 2020 Adam van der Kruk aka TacB0sS
8
+ *
9
+ * Licensed under the Apache License, Version 2.0 (the "License");
10
+ * you may not use this file except in compliance with the License.
11
+ * You may obtain a copy of the License at
12
+ *
13
+ * http://www.apache.org/licenses/LICENSE-2.0
14
+ *
15
+ * Unless required by applicable law or agreed to in writing, software
16
+ * distributed under the License is distributed on an "AS IS" BASIS,
17
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ * See the License for the specific language governing permissions and
19
+ * limitations under the License.
20
+ */
21
+ import { __stringify, _values, ApiException, Module } from '@nu-art/ts-common';
22
+ import { _EmptyQuery } from '@nu-art/firebase-shared';
23
+ import { DBApiDefGeneratorIDBV3 } from '@nu-art/thunderstorm-shared';
24
+ import { addRoutes } from '../ModuleBE_APIs.js';
25
+ import { createBodyServerApi, createQueryServerApi } from '../../core/typed-api.js';
26
+ /**
27
+ * A base class used for implementing CRUD operations on a db module collection.
28
+ *
29
+ * By default, it exposes API endpoints for creating, deleting, updating, querying and querying for unique document.
30
+ */
31
+ export class ModuleBE_BaseApi_Class extends Module {
32
+ dbModule;
33
+ apiDef;
34
+ constructor(dbModule, version) {
35
+ super(`GenApiV3(${dbModule.getName()})`);
36
+ this.dbModule = dbModule;
37
+ this.apiDef = DBApiDefGeneratorIDBV3(this.dbModule.dbDef, version);
38
+ }
39
+ init() {
40
+ this.logDebug(`Adding routes : ${this.apiDef.v1.query.path}`);
41
+ addRoutes([
42
+ createBodyServerApi(this.apiDef.v1.query, async (queryBody) => {
43
+ const items = await this.dbModule.query.where(queryBody);
44
+ await this.dbModule.upgradeInstances(items);
45
+ return items;
46
+ }),
47
+ createQueryServerApi(this.apiDef.v1.queryUnique, async (queryObject) => {
48
+ const toReturnItem = await this.dbModule.query.unique(queryObject._id);
49
+ if (!toReturnItem)
50
+ throw new ApiException(404, `Could not find ${this.dbModule.collection.dbDef.entityName} with _id: ${queryObject._id}`);
51
+ return toReturnItem;
52
+ }),
53
+ createBodyServerApi(this.apiDef.v1.upsert, this.dbModule.set.item),
54
+ createBodyServerApi(this.apiDef.v1.upsertAll, (body) => this.dbModule.set.all(body)),
55
+ createQueryServerApi(this.apiDef.v1.delete, (toDeleteObject) => this.dbModule.delete.unique(toDeleteObject._id)),
56
+ createBodyServerApi(this.apiDef.v1.deleteQuery, this._deleteQuery),
57
+ createQueryServerApi(this.apiDef.v1.deleteAll, () => this.dbModule.delete.query(_EmptyQuery)),
58
+ createQueryServerApi(this.apiDef.v1.metadata, this._metadata)
59
+ ]);
60
+ }
61
+ _metadata = async () => {
62
+ return { ...this.dbModule.dbDef.metadata };
63
+ };
64
+ _deleteQuery = async (query) => {
65
+ if (!query.where)
66
+ throw new ApiException(400, `Cannot delete without a where clause, using query: ${__stringify(query)}`);
67
+ if (_values(query.where).filter(v => v === undefined || v === null).length > 0)
68
+ throw new ApiException(400, `Cannot delete with property value undefined or null, using query: ${__stringify(query)}`);
69
+ return this.dbModule.delete.query(query);
70
+ };
71
+ }
72
+ export const createApisForDBModuleV3 = (dbModule, version) => {
73
+ return new ModuleBE_BaseApi_Class(dbModule, version);
74
+ };
@@ -0,0 +1,78 @@
1
+ import { FirestoreQuery } from '@nu-art/firebase-shared';
2
+ import { DBDef_V3, DBProto, Module } from '@nu-art/ts-common';
3
+ import { CollectionActionType, FirestoreCollectionV3, PostWriteProcessingData } from '@nu-art/firebase-backend/firestore-v3/FirestoreCollectionV3';
4
+ import { DBApiBEConfig } from '../../core/db-def.js';
5
+ import { Response_DBSync } from '@nu-art/thunderstorm-shared/sync-manager/types';
6
+ import { Transaction } from 'firebase-admin/firestore';
7
+ import { EntityDependencyCollection } from '../collection-actions/dispatcher.js';
8
+ import { DBEntityDependencies } from '../../shared.js';
9
+ export type BaseDBApiConfigV3 = {
10
+ projectId?: string;
11
+ chunksSize: number;
12
+ };
13
+ export type DBApiConfigV3<Proto extends DBProto<any>> = BaseDBApiConfigV3 & DBApiBEConfig<Proto>;
14
+ /**
15
+ * An abstract base class used for implementing CRUD operations on a specific collection.
16
+ *
17
+ * By default, it exposes API endpoints for creating, deleting, updating, querying and querying for unique document.
18
+ */
19
+ export declare abstract class ModuleBE_BaseDB<Proto extends DBProto<any>, ConfigType = any, Config extends ConfigType & DBApiConfigV3<Proto> = ConfigType & DBApiConfigV3<Proto>> extends Module<Config> implements EntityDependencyCollection {
20
+ private readonly ModuleBE_BaseDBV2;
21
+ collection: FirestoreCollectionV3<Proto>;
22
+ dbDef: DBDef_V3<Proto>;
23
+ query: FirestoreCollectionV3<Proto>['query'];
24
+ create: FirestoreCollectionV3<Proto>['create'];
25
+ set: FirestoreCollectionV3<Proto>['set'];
26
+ delete: FirestoreCollectionV3<Proto>['delete'];
27
+ doc: FirestoreCollectionV3<Proto>['doc'];
28
+ runTransaction: FirestoreCollectionV3<Proto>['runTransaction'];
29
+ protected constructor(dbDef: DBDef_V3<Proto>, appConfig?: BaseDBApiConfigV3);
30
+ __collectEntityDependencies: <T extends DBProto<any>>(type: T["dbKey"], itemIds: string[], transaction?: Transaction) => Promise<DBEntityDependencies | undefined>;
31
+ private mapConflicts;
32
+ /**
33
+ * Executed during the initialization of the module.
34
+ * The collection reference is set in this method.
35
+ */
36
+ init(): void;
37
+ querySync: (syncQuery: FirestoreQuery<Proto["dbType"]>) => Promise<Response_DBSync<Proto["dbType"]>>;
38
+ private _preWriteProcessing;
39
+ /**
40
+ * Override this method to customize the processing that should be done before create, set or update.
41
+ *
42
+ * @param transaction - The transaction object.
43
+ * @param dbInstance - The DB entry for which the uniqueness is being asserted.
44
+ * @param originalDbInstance - The DB instance fetched from remote firestore.
45
+ */
46
+ protected preWriteProcessing(dbInstance: Proto['uiType'], originalDbInstance: Proto['dbType'], transaction?: Transaction): Promise<void>;
47
+ private _postWriteProcessing;
48
+ /**
49
+ * Override this method to customize processing that should be done after create, set, update or delete.
50
+ * @param data
51
+ * @param actionType create/set/update/delete
52
+ * @param transaction
53
+ */
54
+ protected postWriteProcessing(data: PostWriteProcessingData<Proto>, actionType: CollectionActionType, transaction?: Transaction): Promise<void>;
55
+ manipulateQuery(query: FirestoreQuery<Proto['dbType']>): FirestoreQuery<Proto['dbType']>;
56
+ preUpsertProcessing: never;
57
+ /**
58
+ * Override this method to provide actions or assertions to be executed before the deletion happens.
59
+ * @param transaction - The transaction object
60
+ * @param dbItems - The DB entry that is going to be deleted.
61
+ */
62
+ canDeleteItems(dbItems: Proto['dbType'][], transaction?: Transaction): Promise<void>;
63
+ collectDependencies(dbInstances: Proto['dbType'][], transaction?: Transaction): Promise<DBEntityDependencies | undefined>;
64
+ private versionUpgrades;
65
+ /**
66
+ * Upgrades the entity from the given version to the next one (to the same version if the given version is the latest)
67
+ * @param version - The version we start from
68
+ * @param processor
69
+ */
70
+ registerVersionUpgradeProcessor<K extends Proto['versions']['versions'][number]>(version: K, processor: (items: Proto['versions']['types'][K][]) => Promise<void>): void;
71
+ /**
72
+ * Check if the collection has at least one item without the latest version. Version[0] is the latest version.
73
+ */
74
+ isCollectionUpToDate: () => Promise<boolean>;
75
+ upgradeCollection: (force?: boolean) => Promise<void>;
76
+ processCollection: (processInstances: (instances: Proto["dbType"][]) => Promise<void>) => Promise<void>;
77
+ upgradeInstances(instances: Proto['dbType'][], force?: boolean): Promise<Proto["dbType"][]>;
78
+ }