@my-devkit/firebase 1.0.80 → 1.0.84

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 (200) hide show
  1. package/.eslintrc.js +15 -0
  2. package/.vscode/settings.json +7 -0
  3. package/{aggregate.d.ts → dist/aggregate.d.ts} +0 -0
  4. package/{aggregate.js → dist/aggregate.js} +0 -0
  5. package/{aggregate.js.map → dist/aggregate.js.map} +0 -0
  6. package/{app-factory.d.ts → dist/app-factory.d.ts} +0 -0
  7. package/{app-factory.js → dist/app-factory.js} +0 -0
  8. package/{app-factory.js.map → dist/app-factory.js.map} +0 -0
  9. package/{bus.d.ts → dist/bus.d.ts} +0 -0
  10. package/{bus.js → dist/bus.js} +0 -0
  11. package/{bus.js.map → dist/bus.js.map} +0 -0
  12. package/{constants.d.ts → dist/constants.d.ts} +0 -0
  13. package/{constants.js → dist/constants.js} +0 -0
  14. package/{constants.js.map → dist/constants.js.map} +0 -0
  15. package/{context.d.ts → dist/context.d.ts} +0 -0
  16. package/{context.js → dist/context.js} +0 -0
  17. package/{context.js.map → dist/context.js.map} +0 -0
  18. package/{decorators → dist/decorators}/controller/body.d.ts +0 -0
  19. package/{decorators → dist/decorators}/controller/body.js +0 -0
  20. package/{decorators → dist/decorators}/controller/body.js.map +0 -0
  21. package/{decorators → dist/decorators}/controller/body.spec.d.ts +0 -0
  22. package/{decorators → dist/decorators}/controller/body.spec.js +0 -0
  23. package/{decorators → dist/decorators}/controller/body.spec.js.map +0 -0
  24. package/{decorators → dist/decorators}/controller/controller.d.ts +0 -0
  25. package/{decorators → dist/decorators}/controller/controller.js +0 -0
  26. package/{decorators → dist/decorators}/controller/controller.js.map +0 -0
  27. package/{decorators → dist/decorators}/controller/controller.spec.d.ts +0 -0
  28. package/{decorators → dist/decorators}/controller/controller.spec.js +0 -0
  29. package/{decorators → dist/decorators}/controller/controller.spec.js.map +0 -0
  30. package/{decorators → dist/decorators}/controller/get.d.ts +0 -0
  31. package/{decorators → dist/decorators}/controller/get.js +0 -0
  32. package/{decorators → dist/decorators}/controller/get.js.map +0 -0
  33. package/{decorators → dist/decorators}/controller/get.spec.d.ts +0 -0
  34. package/{decorators → dist/decorators}/controller/get.spec.js +0 -0
  35. package/{decorators → dist/decorators}/controller/get.spec.js.map +0 -0
  36. package/{decorators → dist/decorators}/controller/index.d.ts +0 -0
  37. package/{decorators → dist/decorators}/controller/index.js +0 -0
  38. package/{decorators → dist/decorators}/controller/index.js.map +0 -0
  39. package/{decorators → dist/decorators}/controller/param.d.ts +0 -0
  40. package/{decorators → dist/decorators}/controller/param.js +0 -0
  41. package/{decorators → dist/decorators}/controller/param.js.map +0 -0
  42. package/{decorators → dist/decorators}/controller/param.spec.d.ts +0 -0
  43. package/{decorators → dist/decorators}/controller/param.spec.js +0 -0
  44. package/{decorators → dist/decorators}/controller/param.spec.js.map +0 -0
  45. package/{decorators → dist/decorators}/controller/post.d.ts +0 -0
  46. package/{decorators → dist/decorators}/controller/post.js +0 -0
  47. package/{decorators → dist/decorators}/controller/post.js.map +0 -0
  48. package/{decorators → dist/decorators}/controller/post.spec.d.ts +0 -0
  49. package/{decorators → dist/decorators}/controller/post.spec.js +0 -0
  50. package/{decorators → dist/decorators}/controller/post.spec.js.map +0 -0
  51. package/{decorators → dist/decorators}/controller/query.d.ts +0 -0
  52. package/{decorators → dist/decorators}/controller/query.js +0 -0
  53. package/{decorators → dist/decorators}/controller/query.js.map +0 -0
  54. package/{decorators → dist/decorators}/controller/query.spec.d.ts +0 -0
  55. package/{decorators → dist/decorators}/controller/query.spec.js +0 -0
  56. package/{decorators → dist/decorators}/controller/query.spec.js.map +0 -0
  57. package/{decorators → dist/decorators}/handler/command-handler.d.ts +0 -0
  58. package/{decorators → dist/decorators}/handler/command-handler.js +4 -3
  59. package/dist/decorators/handler/command-handler.js.map +1 -0
  60. package/{decorators → dist/decorators}/handler/command-handler.spec.d.ts +0 -0
  61. package/{decorators → dist/decorators}/handler/command-handler.spec.js +0 -0
  62. package/{decorators → dist/decorators}/handler/command-handler.spec.js.map +0 -0
  63. package/{decorators → dist/decorators}/handler/event-handler.d.ts +0 -0
  64. package/{decorators → dist/decorators}/handler/event-handler.js +4 -3
  65. package/dist/decorators/handler/event-handler.js.map +1 -0
  66. package/{decorators → dist/decorators}/handler/event-handler.spec.d.ts +0 -0
  67. package/{decorators → dist/decorators}/handler/event-handler.spec.js +0 -0
  68. package/{decorators → dist/decorators}/handler/event-handler.spec.js.map +0 -0
  69. package/{decorators → dist/decorators}/handler/index.d.ts +0 -0
  70. package/{decorators → dist/decorators}/handler/index.js +0 -0
  71. package/{decorators → dist/decorators}/handler/index.js.map +0 -0
  72. package/{decorators → dist/decorators}/index.d.ts +0 -0
  73. package/{decorators → dist/decorators}/index.js +0 -0
  74. package/{decorators → dist/decorators}/index.js.map +0 -0
  75. package/{decorators → dist/decorators}/injectable.d.ts +0 -0
  76. package/{decorators → dist/decorators}/injectable.js +0 -0
  77. package/{decorators → dist/decorators}/injectable.js.map +0 -0
  78. package/{decorators → dist/decorators}/module.d.ts +0 -0
  79. package/{decorators → dist/decorators}/module.js +0 -0
  80. package/{decorators → dist/decorators}/module.js.map +0 -0
  81. package/{decorators → dist/decorators}/transactional-client.d.ts +0 -0
  82. package/{decorators → dist/decorators}/transactional-client.js +0 -0
  83. package/{decorators → dist/decorators}/transactional-client.js.map +0 -0
  84. package/{execution-mode-enum.d.ts → dist/execution-mode-enum.d.ts} +0 -0
  85. package/{execution-mode-enum.js → dist/execution-mode-enum.js} +0 -0
  86. package/{execution-mode-enum.js.map → dist/execution-mode-enum.js.map} +0 -0
  87. package/{firestore-client.d.ts → dist/firestore-client.d.ts} +0 -0
  88. package/{firestore-client.js → dist/firestore-client.js} +0 -0
  89. package/{firestore-client.js.map → dist/firestore-client.js.map} +0 -0
  90. package/{handler-helper.d.ts → dist/handler-helper.d.ts} +0 -0
  91. package/{handler-helper.js → dist/handler-helper.js} +0 -0
  92. package/{handler-helper.js.map → dist/handler-helper.js.map} +0 -0
  93. package/{index.d.ts → dist/index.d.ts} +0 -0
  94. package/{index.js → dist/index.js} +0 -0
  95. package/{index.js.map → dist/index.js.map} +0 -0
  96. package/{injector.d.ts → dist/injector.d.ts} +0 -0
  97. package/{injector.js → dist/injector.js} +0 -0
  98. package/{injector.js.map → dist/injector.js.map} +0 -0
  99. package/{interfaces → dist/interfaces}/app-config.d.ts +0 -0
  100. package/{interfaces → dist/interfaces}/app-config.js +0 -0
  101. package/{interfaces → dist/interfaces}/app-config.js.map +0 -0
  102. package/{interfaces → dist/interfaces}/from-array.d.ts +0 -0
  103. package/{interfaces → dist/interfaces}/from-array.js +0 -0
  104. package/{interfaces → dist/interfaces}/from-array.js.map +0 -0
  105. package/{interfaces → dist/interfaces}/index.d.ts +0 -0
  106. package/{interfaces → dist/interfaces}/index.js +0 -0
  107. package/{interfaces → dist/interfaces}/index.js.map +0 -0
  108. package/{interfaces → dist/interfaces}/newable.d.ts +0 -0
  109. package/{interfaces → dist/interfaces}/newable.js +0 -0
  110. package/{interfaces → dist/interfaces}/newable.js.map +0 -0
  111. package/{interfaces → dist/interfaces}/transactional-client.d.ts +0 -0
  112. package/{interfaces → dist/interfaces}/transactional-client.js +0 -0
  113. package/{interfaces → dist/interfaces}/transactional-client.js.map +0 -0
  114. package/{interfaces → dist/interfaces}/type.d.ts +0 -0
  115. package/{interfaces → dist/interfaces}/type.js +0 -0
  116. package/{interfaces → dist/interfaces}/type.js.map +0 -0
  117. package/{reflect.d.ts → dist/reflect.d.ts} +0 -0
  118. package/{reflect.js → dist/reflect.js} +0 -0
  119. package/{reflect.js.map → dist/reflect.js.map} +0 -0
  120. package/{request-method.enum.d.ts → dist/request-method.enum.d.ts} +0 -0
  121. package/{request-method.enum.js → dist/request-method.enum.js} +0 -0
  122. package/{request-method.enum.js.map → dist/request-method.enum.js.map} +0 -0
  123. package/{server → dist/server}/index.d.ts +0 -0
  124. package/{server → dist/server}/index.js +0 -0
  125. package/{server → dist/server}/index.js.map +0 -0
  126. package/{server → dist/server}/middlewares/authentication-middleware.d.ts +0 -0
  127. package/{server → dist/server}/middlewares/authentication-middleware.js +0 -0
  128. package/{server → dist/server}/middlewares/authentication-middleware.js.map +0 -0
  129. package/{server → dist/server}/middlewares/create-context-middleware.d.ts +0 -0
  130. package/{server → dist/server}/middlewares/create-context-middleware.js +0 -0
  131. package/{server → dist/server}/middlewares/create-context-middleware.js.map +0 -0
  132. package/{server → dist/server}/middlewares/error-middleware.d.ts +0 -0
  133. package/{server → dist/server}/middlewares/error-middleware.js +0 -0
  134. package/{server → dist/server}/middlewares/error-middleware.js.map +0 -0
  135. package/{server → dist/server}/middlewares/headers-middleware.d.ts +0 -0
  136. package/{server → dist/server}/middlewares/headers-middleware.js +0 -0
  137. package/{server → dist/server}/middlewares/headers-middleware.js.map +0 -0
  138. package/{server → dist/server}/middlewares/index.d.ts +0 -0
  139. package/{server → dist/server}/middlewares/index.js +0 -0
  140. package/{server → dist/server}/middlewares/index.js.map +0 -0
  141. package/{server → dist/server}/middlewares/not-found-middleware.d.ts +0 -0
  142. package/{server → dist/server}/middlewares/not-found-middleware.js +0 -0
  143. package/{server → dist/server}/middlewares/not-found-middleware.js.map +0 -0
  144. package/{server → dist/server}/server.d.ts +0 -0
  145. package/{server → dist/server}/server.js +0 -0
  146. package/{server → dist/server}/server.js.map +0 -0
  147. package/dist/tsconfig.tsbuildinfo +1 -0
  148. package/package.json +6 -6
  149. package/src/aggregate.ts +54 -0
  150. package/src/app-factory.ts +252 -0
  151. package/src/bus.ts +70 -0
  152. package/src/constants.ts +5 -0
  153. package/src/context.ts +108 -0
  154. package/src/decorators/controller/body.spec.ts +52 -0
  155. package/src/decorators/controller/body.ts +17 -0
  156. package/src/decorators/controller/controller.spec.ts +19 -0
  157. package/src/decorators/controller/controller.ts +10 -0
  158. package/src/decorators/controller/get.spec.ts +32 -0
  159. package/src/decorators/controller/get.ts +8 -0
  160. package/src/decorators/controller/index.ts +8 -0
  161. package/src/decorators/controller/param.spec.ts +51 -0
  162. package/src/decorators/controller/param.ts +21 -0
  163. package/src/decorators/controller/post.spec.ts +60 -0
  164. package/src/decorators/controller/post.ts +8 -0
  165. package/src/decorators/controller/query.spec.ts +51 -0
  166. package/src/decorators/controller/query.ts +21 -0
  167. package/src/decorators/handler/command-handler.spec.ts +101 -0
  168. package/src/decorators/handler/command-handler.ts +23 -0
  169. package/src/decorators/handler/event-handler.spec.ts +104 -0
  170. package/src/decorators/handler/event-handler.ts +24 -0
  171. package/src/decorators/handler/index.ts +2 -0
  172. package/src/decorators/index.ts +5 -0
  173. package/src/decorators/injectable.ts +7 -0
  174. package/src/decorators/module.ts +29 -0
  175. package/src/decorators/transactional-client.ts +7 -0
  176. package/src/execution-mode-enum.ts +4 -0
  177. package/src/firestore-client.ts +125 -0
  178. package/src/handler-helper.ts +68 -0
  179. package/src/index.ts +7 -0
  180. package/src/injector.ts +31 -0
  181. package/src/interfaces/app-config.ts +1 -0
  182. package/src/interfaces/from-array.ts +1 -0
  183. package/src/interfaces/index.ts +4 -0
  184. package/src/interfaces/newable.ts +1 -0
  185. package/src/interfaces/transactional-client.ts +3 -0
  186. package/src/interfaces/type.ts +3 -0
  187. package/src/reflect.ts +164 -0
  188. package/src/request-method.enum.ts +4 -0
  189. package/src/server/index.ts +1 -0
  190. package/src/server/middlewares/authentication-middleware.ts +47 -0
  191. package/src/server/middlewares/create-context-middleware.ts +16 -0
  192. package/src/server/middlewares/error-middleware.ts +33 -0
  193. package/src/server/middlewares/headers-middleware.ts +16 -0
  194. package/src/server/middlewares/index.ts +5 -0
  195. package/src/server/middlewares/not-found-middleware.ts +9 -0
  196. package/src/server/server.ts +96 -0
  197. package/tsconfig.json +24 -0
  198. package/decorators/handler/command-handler.js.map +0 -1
  199. package/decorators/handler/event-handler.js.map +0 -1
  200. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,104 @@
1
+ import { Event } from '@my-devkit/core';
2
+ import { expect } from 'chai';
3
+
4
+ import { ExecutionMode } from '../../execution-mode-enum';
5
+ import { reflect } from '../../reflect';
6
+ import { EventHandler } from './event-handler';
7
+
8
+ describe('Given a EventHandler decorator', () => {
9
+
10
+ describe('When decorator is called', () => {
11
+
12
+ describe('And decorated method has no parameter', () => {
13
+ it('Then we should retrieve information via reflect', async () => {
14
+
15
+ try {
16
+ class TestClass {
17
+ @EventHandler(ExecutionMode.Asynchronous)
18
+ public created(): string {
19
+ return `created`;
20
+ }
21
+ }
22
+
23
+ reflect(TestClass).getHandlerConfiguration();
24
+ } catch (error) {
25
+ expect(error.message).equal('TestClass.created should have one argument!');
26
+ }
27
+ });
28
+ });
29
+
30
+ describe('And decorated method has more than 1 parameter', () => {
31
+ it('Then we should retrieve information via reflect', async () => {
32
+
33
+ try {
34
+ class TestClass {
35
+ @EventHandler(ExecutionMode.Asynchronous)
36
+ public created(param1: string, param2: string): string {
37
+ return `created ${param1} / ${param2}`;
38
+ }
39
+ }
40
+
41
+ reflect(TestClass).getHandlerConfiguration();
42
+ } catch (error) {
43
+ expect(error.message).equal('TestClass.created should have only one argument!');
44
+ }
45
+ });
46
+ });
47
+
48
+ describe('And decorated method parameter is not a command', () => {
49
+ it('Then we should retrieve information via reflect', async () => {
50
+
51
+ try {
52
+ class FakeEvent {
53
+ test: string;
54
+ }
55
+
56
+ class TestClass {
57
+ @EventHandler(ExecutionMode.Asynchronous)
58
+ public created(fakeEvent: FakeEvent): string {
59
+ return `created ${fakeEvent}`;
60
+ }
61
+ }
62
+
63
+ reflect(TestClass).getHandlerConfiguration();
64
+ } catch (error) {
65
+ expect(error.message).equal('TestClass.created should have an event as argument!');
66
+ }
67
+ });
68
+ });
69
+
70
+ describe('And handler is correctly implemented', () => {
71
+
72
+ class CreatedEvent extends Event {
73
+ }
74
+
75
+ class UpdatedEvent extends Event {
76
+ }
77
+
78
+ class TestClass {
79
+ @EventHandler(ExecutionMode.Asynchronous)
80
+ public created(event: CreatedEvent): string {
81
+ return `created: ${event}`;
82
+ }
83
+
84
+ @EventHandler(ExecutionMode.Synchronous)
85
+ public updated(event: UpdatedEvent): string {
86
+ return `updated: ${event}`;
87
+ }
88
+ }
89
+ it('Then we should retrieve information via reflect', async () => {
90
+ const config = reflect(TestClass).getHandlerConfiguration();
91
+ expect(config.eventHandlers.size).equal(2, 'Wrong route count detected');
92
+
93
+ expect(config.eventHandlers[0].methodName).equal('created');
94
+ expect(config.eventHandlers[0].executionMode).equal(ExecutionMode.Asynchronous);
95
+ expect(config.eventHandlers[0].event.constructor.name).equal('CreatedEvent');
96
+
97
+ expect(config.eventHandlers[1].methodName).equal('updated');
98
+ expect(config.eventHandlers[1].executionMode).equal(ExecutionMode.Synchronous);
99
+ expect(config.eventHandlers[1].event.constructor.name).equal('UpdatedEvent');
100
+
101
+ });
102
+ });
103
+ });
104
+ });
@@ -0,0 +1,24 @@
1
+ import { Event } from '@my-devkit/core';
2
+
3
+ import { ExecutionMode } from '../../execution-mode-enum';
4
+ import { reflect } from '../../reflect';
5
+
6
+ export function EventHandler(mode: ExecutionMode): MethodDecorator {
7
+ return (target, propertyKey) => {
8
+ const paramTypes = reflect(target, propertyKey).getParameters();
9
+
10
+ if (paramTypes.length === 0) {
11
+ throw new Error(`${target.constructor.name}.${propertyKey.toString()} should have one argument!`);
12
+ }
13
+
14
+ if (paramTypes.length > 1) {
15
+ throw new Error(`${target.constructor.name}.${propertyKey.toString()} should have only one argument!`);
16
+ }
17
+
18
+ if (!(paramTypes[0].prototype instanceof Event)) {
19
+ throw new Error(`${target.constructor.name}.${propertyKey.toString()} should have an event as argument!`);
20
+ }
21
+
22
+ reflect(target, propertyKey).registerEventHandler(<any>paramTypes[0].prototype, mode);
23
+ }
24
+ }
@@ -0,0 +1,2 @@
1
+ export * from './command-handler';
2
+ export * from './event-handler';
@@ -0,0 +1,5 @@
1
+ export * from './controller';
2
+ export * from './handler';
3
+ export * from './injectable';
4
+ export * from './module';
5
+ export * from './transactional-client';
@@ -0,0 +1,7 @@
1
+ import { reflect } from '../reflect';
2
+
3
+ export function Injectable(): ClassDecorator {
4
+ return target => {
5
+ reflect(target).registerInjectable();
6
+ };
7
+ }
@@ -0,0 +1,29 @@
1
+ import { Type } from '../interfaces';
2
+
3
+ export interface ModuleOptions {
4
+ /**
5
+ * Optional list of imported modules that export the providers which are
6
+ * required in this module.
7
+ */
8
+ imports?: Type[];
9
+ /**
10
+ * Optional list of controllers defined in this module which have to be
11
+ * instantiated.
12
+ */
13
+ controllers?: Type[];
14
+
15
+ /**
16
+ * Optional list of handlers defined in this module which have to be instantiated.
17
+ */
18
+ handlers?: Type[];
19
+ }
20
+
21
+ export function Module(options: ModuleOptions): ClassDecorator {
22
+ return target => {
23
+ for (const key in options) {
24
+ if (options.hasOwnProperty(key)) {
25
+ Reflect.defineMetadata(key, options[key], target);
26
+ }
27
+ }
28
+ };
29
+ }
@@ -0,0 +1,7 @@
1
+ import { reflect } from '../reflect';
2
+
3
+ export function TransactionalClient(): ClassDecorator {
4
+ return target => {
5
+ reflect(target).registerTransactionalClient();
6
+ };
7
+ }
@@ -0,0 +1,4 @@
1
+ export enum ExecutionMode {
2
+ Synchronous = <any>'Synchronous',
3
+ Asynchronous = <any>'Asynchronous'
4
+ }
@@ -0,0 +1,125 @@
1
+ import { deserialize, Document, Logger, NotFoundError, serialize } from '@my-devkit/core';
2
+ import { getFirestore } from 'firebase-admin/firestore';
3
+
4
+ import { Injectable, TransactionalClient } from './decorators';
5
+ import { ITransactionalClient } from './interfaces';
6
+
7
+
8
+ @TransactionalClient()
9
+ @Injectable()
10
+ export class FirestoreClient implements ITransactionalClient {
11
+ private db = getFirestore();
12
+ private operations: FirestoreClient.Operation[] = [];
13
+ private cachedDocuments = new Map<string, Document>();
14
+
15
+ public async save(): Promise<void> {
16
+ Logger.info(`Saving ${this.operations.length} operations`);
17
+
18
+ for (const operation of this.operations) {
19
+ if (operation.action === FirestoreClient.Action.Create) {
20
+ Logger.info(`Create document ${operation.document._path}`);
21
+ await this.db.doc(operation.document._path).set(serialize(operation.document));
22
+ } else if (operation.action === FirestoreClient.Action.Update) {
23
+ Logger.info(`Update document ${operation.document._path}`);
24
+ await this.db.doc(operation.document._path).set(serialize(operation.document));
25
+ } else if (operation.action === FirestoreClient.Action.Delete) {
26
+ Logger.info(`Delete document ${operation.document._path}`);
27
+ await this.db.doc(operation.document._path).delete();
28
+ } else {
29
+ throw new Error(`Transactional Repository: Not supported action ${operation.action}`);
30
+ }
31
+ }
32
+ }
33
+
34
+ public async _find<T>(path: string): Promise<T> {
35
+ Logger.info(`Repository: find ${path}`);
36
+ if (this.cachedDocuments.has(path)) {
37
+ Logger.info(`Repository: document returned from transaction`);
38
+ return <any>this.cachedDocuments.get(path);
39
+ }
40
+ const doc = await this.db.doc(path).get();
41
+ const result = doc.data();
42
+ return result ? deserialize(result) : null;
43
+ }
44
+
45
+ public async _get<T>(path: string): Promise<T> {
46
+ Logger.info(`Repository: get ${path}`);
47
+ const result = await this._find<T>(path);
48
+ if (!result) {
49
+ throw new NotFoundError(`Repository: document ${path} not found`);
50
+ }
51
+
52
+ return result;
53
+ }
54
+
55
+ public async _getAll<T>(collection: string): Promise<T[]> {
56
+ Logger.info(`Repository: getAll ${collection}`);
57
+ const documents: T[] = [];
58
+ const querySnapshot = await this.getCollectionReference(collection).get();
59
+ querySnapshot.forEach(doc => {
60
+ documents.push(deserialize(doc.data()));
61
+ });
62
+ return documents;
63
+ }
64
+
65
+ public async _getWhere<T>(collection: string, whereClauses: [string, FirestoreClient.Operator, any | any[]][] = [], orderBy: string = null): Promise<T[]> {
66
+ Logger.info(`Repository: getWhere ${collection} whereClauses: ${JSON.stringify(whereClauses)}`);
67
+ const documents: T[] = [];
68
+
69
+ let query = this.getCollectionReference(collection);
70
+
71
+ whereClauses.forEach(where => {
72
+ query = query.where(where[0], where[1], where[2]);
73
+ });
74
+
75
+ if (orderBy) {
76
+ query = query.orderBy(orderBy);
77
+ }
78
+
79
+ const querySnapshot = await query.get();
80
+ querySnapshot.forEach(doc => {
81
+ documents.push(deserialize(doc.data()));
82
+ });
83
+
84
+ return documents;
85
+ }
86
+
87
+
88
+ public createDocument(document: Document): void {
89
+ this.operations.push({ document, action: FirestoreClient.Action.Create });
90
+ this.cachedDocuments.set(document._path, document);
91
+ }
92
+
93
+ public updateDocument(document: Document): void {
94
+ this.operations.push({ document, action: FirestoreClient.Action.Update });
95
+ this.cachedDocuments.set(document._path, document);
96
+ }
97
+
98
+ public deleteDocument(document: Document): void {
99
+ this.operations.push({ document, action: FirestoreClient.Action.Delete });
100
+ this.cachedDocuments.delete(document._path);
101
+ }
102
+
103
+ private getCollectionReference(collection: string): FirebaseFirestore.CollectionReference | FirebaseFirestore.Query {
104
+ if (collection.startsWith('**/')) {
105
+ return this.db.collectionGroup(collection.replace('**/', ''));
106
+ } else {
107
+ return this.db.collection(collection);
108
+ }
109
+ }
110
+ }
111
+
112
+ export namespace FirestoreClient {
113
+
114
+ export interface Operation {
115
+ document: Document;
116
+ action: Action;
117
+ }
118
+
119
+ export enum Action {
120
+ Create = <any>'Create',
121
+ Update = <any>'Update',
122
+ Delete = <any>'Delete'
123
+ }
124
+ export type Operator = FirebaseFirestore.WhereFilterOp;
125
+ }
@@ -0,0 +1,68 @@
1
+ import { Logger } from '@my-devkit/core';
2
+
3
+ import { Aggregate } from './aggregate';
4
+ import { Context } from './context';
5
+ import { ExecutionMode } from './execution-mode-enum';
6
+ import { FirestoreClient } from './firestore-client';
7
+
8
+ export class HandlerHelper {
9
+ public static async create<A extends Aggregate, R>(aggregate: A, callback: (a: A) => Promise<R>): Promise<R> {
10
+ const result = await callback(aggregate);
11
+
12
+ this.client.createDocument(aggregate);
13
+ await this.publishEvents(aggregate);
14
+
15
+ return result;
16
+ }
17
+
18
+ public static async update<A extends Aggregate, R>(aggregateOrId: A | string, callback: (a: A) => Promise<R>): Promise<R> {
19
+ const aggregate = await this.getAggregate(aggregateOrId);
20
+ if (aggregate.events.length > 0) {
21
+ Logger.warn(`${aggregate._type} ${aggregate.id} has ${aggregate.events.length} events!!`);
22
+ }
23
+ const hashBefore = aggregate.hash;
24
+
25
+ const result = await callback(aggregate);
26
+
27
+ if (aggregate.hash !== hashBefore) {
28
+ this.client.updateDocument(aggregate);
29
+ await this.publishEvents(aggregate);
30
+ } else {
31
+ Logger.debug(`${aggregate._type} ${aggregate.id} didn't change, ${aggregate.events.length} events skipped`);
32
+ aggregate.clearEvents();
33
+ }
34
+
35
+ return result;
36
+ }
37
+
38
+ public static async delete<A extends Aggregate, R>(aggregateOrId: A | string, callback: (a: A) => Promise<R>): Promise<R> {
39
+ const aggregate = await this.getAggregate(aggregateOrId);
40
+
41
+ const result = await callback(aggregate);
42
+
43
+ this.client.deleteDocument(aggregate);
44
+ await this.publishEvents(aggregate);
45
+
46
+ return result;
47
+ }
48
+
49
+ private static get client(): FirestoreClient {
50
+ return Context.resolve(FirestoreClient);
51
+ }
52
+
53
+ private static async publishEvents<A extends Aggregate>(aggregate: A): Promise<void> {
54
+ const events = aggregate.events;
55
+ aggregate.clearEvents();
56
+
57
+ for (const event of events) {
58
+ await Context.bus.publish(event, ExecutionMode.Synchronous);
59
+ }
60
+ }
61
+
62
+ private static async getAggregate<A extends Aggregate>(aggregateOrId: A | string): Promise<A> {
63
+ if (typeof aggregateOrId === 'string' || aggregateOrId instanceof String) {
64
+ return this.client._find<A>(`/aggregates/${aggregateOrId}`);
65
+ }
66
+ return aggregateOrId;
67
+ }
68
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './decorators';
2
+ export * from './aggregate';
3
+ export * from './context';
4
+ export * from './app-factory';
5
+ export * from './firestore-client';
6
+ export * from './execution-mode-enum';
7
+ export * from './handler-helper';
@@ -0,0 +1,31 @@
1
+ import { ITransactionalClient, Type } from './interfaces';
2
+ import { reflect } from './reflect';
3
+
4
+ export class Injector {
5
+ private instances = new Map<string, any>();
6
+ private transactionalClients: ITransactionalClient[] = [];
7
+
8
+ public resolve<T>(target: Type<T>): T {
9
+ const injectableId = reflect(target).getInjectableId();
10
+ if (!injectableId) {
11
+ throw new Error(`${target.name} is not injectable`);
12
+ }
13
+
14
+ if (!this.instances.has(injectableId)) {
15
+ const tokens = reflect(target).getParameters();
16
+ const injections = tokens.map(token => this.resolve<any>(token));
17
+ const instance = new target(...injections);
18
+ this.instances.set(injectableId, instance);
19
+
20
+ if (reflect(target).isTransactionalClient()) {
21
+ this.transactionalClients.push(<any>instance);
22
+ }
23
+ }
24
+
25
+ return this.instances.get(injectableId);
26
+ }
27
+
28
+ public getTransactionalClients(): ITransactionalClient[] {
29
+ return [...this.transactionalClients];
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+ export type FromArray<T extends ReadonlyArray<unknown>> = T extends ReadonlyArray<infer FromArray> ? FromArray : never;
@@ -0,0 +1,4 @@
1
+ export * from './newable';
2
+ export * from './from-array';
3
+ export * from './type';
4
+ export * from './transactional-client';
@@ -0,0 +1 @@
1
+ export type Newable<T> = new () => T;
@@ -0,0 +1,3 @@
1
+ export interface ITransactionalClient {
2
+ save(): Promise<void>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export interface Type<T = any> extends Function {
2
+ new(...args: any[]): T;
3
+ }
package/src/reflect.ts ADDED
@@ -0,0 +1,164 @@
1
+ import 'reflect-metadata';
2
+
3
+ import { Command, Event, guid } from '@my-devkit/core';
4
+ import { ExecutionMode } from 'execution-mode-enum';
5
+
6
+ import { Type } from './interfaces';
7
+ import { RequestMethod } from './request-method.enum';
8
+ import { Server } from './server';
9
+
10
+ /* eslint-disable @typescript-eslint/ban-types */
11
+ class ReflectHelper<T extends ReflectTarget<any>> {
12
+ private readonly HANDLER_META_DATA = '__handler__';
13
+ private readonly CONTROLLER_META_DATA = '__controller__';
14
+ private readonly INJECTABLE_META_DATA = '__injectable__';
15
+ private readonly TRANSACTIONAL_CLIENT_META_DATA = '__transactional_client__';
16
+
17
+ constructor(private target: T, private methodName: string | symbol) {
18
+
19
+ }
20
+
21
+ public getParameters(): Type[] {
22
+ return Reflect.getMetadata('design:paramtypes', this.target, this.methodName) || [];
23
+ }
24
+
25
+ public registerInjectable(): void {
26
+ this.defineMetaData(this.INJECTABLE_META_DATA, guid());
27
+ }
28
+
29
+ public getInjectableId(): string {
30
+ return this.getMetaData<string>(this.INJECTABLE_META_DATA)
31
+ }
32
+
33
+ public registerTransactionalClient(): void {
34
+ this.defineMetaData(this.TRANSACTIONAL_CLIENT_META_DATA, guid);
35
+ }
36
+
37
+ public isTransactionalClient(): boolean {
38
+ return this.hasMetaData(this.TRANSACTIONAL_CLIENT_META_DATA)
39
+ }
40
+
41
+ public getControllerConfiguration(): ControllerConfiguration {
42
+ if (!this.hasMetaData(this.CONTROLLER_META_DATA)) {
43
+ this.defineMetaData<ControllerConfiguration>(this.CONTROLLER_META_DATA, {
44
+ basePath: null,
45
+ routes: []
46
+ });
47
+ }
48
+
49
+ return this.getMetaData<ControllerConfiguration>(this.CONTROLLER_META_DATA);
50
+ }
51
+
52
+ public registerController(basePath: string): void {
53
+ this.getControllerConfiguration().basePath = basePath;
54
+ }
55
+
56
+ public registerControllerRoute(route: { path: string, requestMethod: RequestMethod }): void {
57
+ this.getControllerRoute().path = route.path;
58
+ this.getControllerRoute().requestMethod = route.requestMethod;
59
+ }
60
+
61
+ public registerControllerRouteArgumentInjector<T>(injector: RequestArgumentInjector<T>, index: number): void {
62
+ this.getControllerRoute().argumentInjectors[index] = injector;
63
+ }
64
+
65
+ public getHandlerConfiguration(): HandlerConfiguration {
66
+ if (!this.hasMetaData(this.HANDLER_META_DATA)) {
67
+ this.defineMetaData<HandlerConfiguration>(this.HANDLER_META_DATA, {
68
+ commandHandlers: new Map<string, CommandHandler>(),
69
+ eventHandlers: new Map<string, EventHandler>()
70
+ });
71
+ }
72
+
73
+ return this.getMetaData<HandlerConfiguration>(this.HANDLER_META_DATA);
74
+ }
75
+
76
+ public registerCommandHandler<C extends Command>(command: new () => C): void {
77
+ this.getHandlerConfiguration().commandHandlers.set(this.methodName.toString(), {
78
+ methodName: this.methodName.toString(),
79
+ command
80
+ });
81
+ }
82
+
83
+ public registerEventHandler<E extends Event>(event: new () => E, executionMode: ExecutionMode): void {
84
+ this.getHandlerConfiguration().eventHandlers.set(this.methodName.toString(), {
85
+ methodName: this.methodName.toString(),
86
+ executionMode,
87
+ event
88
+ });
89
+ }
90
+
91
+ private getControllerRoute(): ControllerRoute {
92
+ const config = this.getControllerConfiguration();
93
+ if (!config.routes.find(r => r.methodName === this.methodName.toString())) {
94
+ config.routes.push({
95
+ methodName: this.methodName.toString(),
96
+ path: null,
97
+ requestMethod: null,
98
+ argumentInjectors: []
99
+ });
100
+ }
101
+
102
+ return config.routes.find(r => r.methodName === this.methodName.toString());
103
+ }
104
+
105
+ private getMetaData<M>(key: string): M {
106
+ return Reflect.getMetadata(key, this.targetConstructor);
107
+ }
108
+
109
+ private defineMetaData<M>(key: string, data: M): void {
110
+ Reflect.defineMetadata(key, data, this.targetConstructor);
111
+ }
112
+
113
+ private hasMetaData(key: string): boolean {
114
+ return Reflect.hasMetadata(key, this.targetConstructor);
115
+ }
116
+
117
+ private get targetConstructor() {
118
+ if (this.target['prototype']) {
119
+ return this.target['prototype'].constructor;
120
+ }
121
+
122
+ if (this.target.constructor) {
123
+ return this.target.constructor;
124
+ }
125
+
126
+ return this.target;
127
+ }
128
+ }
129
+
130
+ export interface ControllerConfiguration {
131
+ basePath: string;
132
+ routes: ControllerRoute[];
133
+ }
134
+
135
+ export interface ControllerRoute {
136
+ path: string;
137
+ methodName: string;
138
+ requestMethod: RequestMethod;
139
+ argumentInjectors: RequestArgumentInjector<any>[];
140
+ }
141
+
142
+ export interface CommandHandler {
143
+ methodName: string;
144
+ command: new () => Command;
145
+ }
146
+
147
+ export interface EventHandler {
148
+ methodName: string;
149
+ executionMode: ExecutionMode;
150
+ event: new () => Event;
151
+ }
152
+
153
+ export interface HandlerConfiguration {
154
+ commandHandlers: Map<string, CommandHandler>;
155
+ eventHandlers: Map<string, EventHandler>;
156
+ }
157
+
158
+ export type RequestArgumentInjector<T = any> = (req: Server.Request) => T;
159
+
160
+ export function reflect<T extends ReflectTarget<any>>(target: T, methodName?: string | symbol): ReflectHelper<T> {
161
+ return new ReflectHelper(target, methodName);
162
+ }
163
+
164
+ type ReflectTarget<T> = T | (new (...args: any[]) => T);
@@ -0,0 +1,4 @@
1
+ export enum RequestMethod {
2
+ GET = 'GET',
3
+ POST = 'POST'
4
+ }
@@ -0,0 +1 @@
1
+ export * from './server';
@@ -0,0 +1,47 @@
1
+ import { Logger, UnauthorizedError } from '@my-devkit/core';
2
+ import { Auth } from 'firebase-admin/auth';
3
+
4
+ import { Context } from '../../context';
5
+ import { Server } from '../server';
6
+
7
+
8
+ export function AuthenticationMiddleware(auth: Auth, publicPaths: string[]): Server.RequestHandler {
9
+ return async (request: Server.Request, _response: Server.Response, next: Server.NextFunction): Promise<void> => {
10
+ const authorizationHeader = request.headers.authorization ? request.headers.authorization.toString() : null;
11
+ Logger.info(`${request.method} ${request.path}: Authentication check`);
12
+
13
+ if (publicPaths.includes(request.path)) {
14
+ Logger.info(`${request.path} is a public path, no authorization check`);
15
+ Context.user = null;
16
+ next();
17
+ } else if (request.method === 'OPTIONS') {
18
+ next();
19
+ } else if (!authorizationHeader) {
20
+ throw new UnauthorizedError('No authorization header found');
21
+ } else if (authorizationHeader.startsWith('Bearer')) {
22
+ const idToken = authorizationHeader.split('Bearer ')[1];
23
+ try {
24
+ const decodedIdToken = await auth.verifyIdToken(idToken, true);
25
+ const userId = decodedIdToken.uid;
26
+ Context.user = await auth.getUser(userId);
27
+ next();
28
+ } catch (error) {
29
+ throw new UnauthorizedError(`Error while verifying token ${error.message ? error.message : null}`);
30
+ }
31
+ } else if (authorizationHeader.startsWith('Basic')) {
32
+ const idToken = authorizationHeader.split('Basic ')[1];
33
+ try {
34
+ const [userId, password] = Buffer.from(idToken, 'base64').toString().split(':');
35
+ if (password !== '20V0anV4M!we') {
36
+ throw new Error('Wrong password!!');
37
+ }
38
+ Context.user = await auth.getUser(userId);
39
+ next();
40
+ } catch (error) {
41
+ throw new UnauthorizedError(`Error while verifying token ${error.message ? error.message : null}`);
42
+ }
43
+ } else {
44
+ throw new UnauthorizedError('Incorrect authorization header');
45
+ }
46
+ };
47
+ }