http-request-manager 18.7.19 → 18.7.21

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 (198) hide show
  1. package/ARCHITECTURE.md +483 -0
  2. package/DATABASE_README.md +1176 -0
  3. package/HTTP_MANAGER_README.md +579 -0
  4. package/HTTP_SINGNALS_MANAGER_README.md +654 -0
  5. package/HTTP_STATE_MANAGER_README.md +948 -0
  6. package/INTERCEPTOR_README.md +549 -0
  7. package/LOCAL_STORAGE_README.md +1056 -0
  8. package/STORE_STATE_MANAGER_README.md +1322 -0
  9. package/UTILS_README.md +1186 -0
  10. package/WS_MANAGER_README.md +613 -0
  11. package/ng-package.json +8 -0
  12. package/package.json +1 -12
  13. package/src/lib/http-request-manager.module.ts +132 -0
  14. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.html +65 -0
  15. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.scss +0 -0
  16. package/src/lib/http-request-services-demo/database-data-demo/database-data-demo.component.ts +224 -0
  17. package/src/lib/http-request-services-demo/http-request-services-demo.component.html +114 -0
  18. package/src/lib/http-request-services-demo/http-request-services-demo.component.scss +6 -0
  19. package/src/lib/http-request-services-demo/http-request-services-demo.component.ts +52 -0
  20. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.html +195 -0
  21. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.scss +17 -0
  22. package/src/lib/http-request-services-demo/local-storage-demo/local-storage-demo.component.ts +206 -0
  23. package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.html +200 -0
  24. package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.scss +17 -0
  25. package/src/lib/http-request-services-demo/local-storage-signals-demo/local-storage-signals-demo.component.ts +212 -0
  26. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.html +53 -0
  27. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.scss +60 -0
  28. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/download-file/download-file.component.ts +72 -0
  29. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-download.module.ts +28 -0
  30. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.html +10 -0
  31. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.scss +29 -0
  32. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/file-downloader.component.ts +100 -0
  33. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/models/download-labels-model.ts +22 -0
  34. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.html +8 -0
  35. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.scss +19 -0
  36. package/src/lib/http-request-services-demo/request-manager-demo/file-downloader/spinner/spinner.component.ts +26 -0
  37. package/src/lib/http-request-services-demo/request-manager-demo/models/app-session.model.ts +30 -0
  38. package/src/lib/http-request-services-demo/request-manager-demo/models/app.model.ts +19 -0
  39. package/src/lib/http-request-services-demo/request-manager-demo/models/get-sample.model.ts +25 -0
  40. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-ai-prompt.ts +19 -0
  41. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-details.ts +24 -0
  42. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client-info.ts +30 -0
  43. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-client.model.ts +49 -0
  44. package/src/lib/http-request-services-demo/request-manager-demo/models/sample-mapper-client-info.ts +33 -0
  45. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.html +392 -0
  46. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.scss +24 -0
  47. package/src/lib/http-request-services-demo/request-manager-demo/request-manager-demo.component.ts +461 -0
  48. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.html +393 -0
  49. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.scss +24 -0
  50. package/src/lib/http-request-services-demo/request-manager-state-demo/request-manager-state-demo.component.ts +421 -0
  51. package/src/lib/http-request-services-demo/request-manager-state-demo/services/state-manager-demo.service.ts +87 -0
  52. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/services/state-data-request.service.ts +120 -0
  53. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.css +0 -0
  54. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.html +3 -0
  55. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-ai-messaging/ws-ai-messaging.component.ts +16 -0
  56. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.css +0 -0
  57. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.html +3 -0
  58. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-chats/ws-chats.component.ts +16 -0
  59. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.css +31 -0
  60. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.html +72 -0
  61. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.scss +41 -0
  62. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.spec.ts +205 -0
  63. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-data-control/ws-data-control.component.ts +77 -0
  64. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.css +11 -0
  65. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.html +96 -0
  66. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.spec.ts +31 -0
  67. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-messaging/ws-messaging.component.ts +229 -0
  68. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.css +30 -0
  69. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.html +172 -0
  70. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.spec.ts +31 -0
  71. package/src/lib/http-request-services-demo/request-manager-ws-demo/components/ws-notifications/ws-notifications.component.ts +239 -0
  72. package/src/lib/http-request-services-demo/request-manager-ws-demo/models/oidc-client.model.ts +31 -0
  73. package/src/lib/http-request-services-demo/request-manager-ws-demo/models/user-data.model.ts +32 -0
  74. package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.css +0 -0
  75. package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.html +84 -0
  76. package/src/lib/http-request-services-demo/request-manager-ws-demo/request-manager-ws-demo.component.ts +41 -0
  77. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/index.ts +3 -0
  78. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/message-service-demo.service.ts +83 -0
  79. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/notification-service-demo.service.ts +147 -0
  80. package/src/lib/http-request-services-demo/request-manager-ws-demo/services/state-service-demo.service.ts +158 -0
  81. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.html +53 -0
  82. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.scss +60 -0
  83. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/download-file/download-file.component.ts +72 -0
  84. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-download.module.ts +28 -0
  85. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.html +10 -0
  86. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.scss +29 -0
  87. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/file-downloader.component.ts +100 -0
  88. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/models/download-labels-model.ts +22 -0
  89. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.html +8 -0
  90. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.scss +19 -0
  91. package/src/lib/http-request-services-demo/request-signals-manager-demo/file-downloader/spinner/spinner.component.ts +26 -0
  92. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app-session.model.ts +30 -0
  93. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/app.model.ts +19 -0
  94. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/get-sample.model.ts +25 -0
  95. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-ai-prompt.ts +19 -0
  96. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-details.ts +24 -0
  97. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client-info.ts +30 -0
  98. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-client.model.ts +49 -0
  99. package/src/lib/http-request-services-demo/request-signals-manager-demo/models/sample-mapper-client-info.ts +33 -0
  100. package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.html +380 -0
  101. package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.scss +24 -0
  102. package/src/lib/http-request-services-demo/request-signals-manager-demo/request-signals-manager-demo.component.ts +410 -0
  103. package/src/lib/http-request-services-demo/store-state-manager-demo/models/settings.model.ts +28 -0
  104. package/src/lib/http-request-services-demo/store-state-manager-demo/services/settings-state.service.ts +48 -0
  105. package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.css +0 -0
  106. package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.html +23 -0
  107. package/src/lib/http-request-services-demo/store-state-manager-demo/store-state-manager-demo.component.ts +36 -0
  108. package/src/lib/index.ts +3 -0
  109. package/src/lib/interceptors/credentials.interceptor.ts +16 -0
  110. package/src/lib/interceptors/index.ts +6 -0
  111. package/src/lib/interceptors/models/error-settings.model.ts +22 -0
  112. package/src/lib/interceptors/models/index.ts +2 -0
  113. package/src/lib/interceptors/proxy-debugger.interceptor.ts +46 -0
  114. package/src/lib/interceptors/request-error.interceptor.ts +65 -0
  115. package/src/lib/interceptors/request-header.interceptor.ts +53 -0
  116. package/src/lib/models/config-http-options.model.ts +42 -0
  117. package/src/lib/models/config-local-storage-options.model.ts +27 -0
  118. package/src/lib/models/config-options.model.ts +27 -0
  119. package/src/lib/models/config-token.model.ts +9 -0
  120. package/src/lib/models/data-type.enum.ts +5 -0
  121. package/src/lib/models/database-storage.model.ts +24 -0
  122. package/src/lib/models/index.ts +12 -0
  123. package/src/lib/models/retry-options.model.ts +22 -0
  124. package/src/lib/services/database-manager-service/database.manager.service.ts +262 -0
  125. package/src/lib/services/database-manager-service/db.storage.service.ts +207 -0
  126. package/src/lib/services/database-manager-service/index.ts +4 -0
  127. package/src/lib/services/database-manager-service/models/index.ts +2 -0
  128. package/src/lib/services/database-manager-service/models/table-schema.ts +33 -0
  129. package/src/lib/services/index.ts +12 -0
  130. package/src/lib/services/local-storage-manager-service/index.ts +4 -0
  131. package/src/lib/services/local-storage-manager-service/local-storage-manager.service.spec.ts +71 -0
  132. package/src/lib/services/local-storage-manager-service/local-storage-manager.service.ts +426 -0
  133. package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.spec.ts +67 -0
  134. package/src/lib/services/local-storage-manager-service/local-storage-signals-manager.service.ts +345 -0
  135. package/src/lib/services/local-storage-manager-service/models/global-store-options.model.ts +30 -0
  136. package/src/lib/services/local-storage-manager-service/models/index.ts +6 -0
  137. package/src/lib/services/local-storage-manager-service/models/setting-options.model.ts +35 -0
  138. package/src/lib/services/local-storage-manager-service/models/storage-data.model.ts +24 -0
  139. package/src/lib/services/local-storage-manager-service/models/storage-option.model.ts +32 -0
  140. package/src/lib/services/local-storage-manager-service/models/storage-type.enum.ts +5 -0
  141. package/src/lib/services/request-manager-services/README.md +268 -0
  142. package/src/lib/services/request-manager-services/http-manager-signals.service.ts +246 -0
  143. package/src/lib/services/request-manager-services/http-manager.service.spec.ts +232 -0
  144. package/src/lib/services/request-manager-services/http-manager.service.ts +274 -0
  145. package/src/lib/services/request-manager-services/index.ts +8 -0
  146. package/src/lib/services/request-manager-services/request-signals.service.ts +214 -0
  147. package/src/lib/services/request-manager-services/request.service.ts +309 -0
  148. package/src/lib/services/request-manager-services/rxjs-operators/countdown.ts +17 -0
  149. package/src/lib/services/request-manager-services/rxjs-operators/delay-retry.ts +16 -0
  150. package/src/lib/services/request-manager-services/rxjs-operators/index.ts +4 -0
  151. package/src/lib/services/request-manager-services/rxjs-operators/request-polling.ts +35 -0
  152. package/src/lib/services/request-manager-services/rxjs-operators/request-streaming.ts +436 -0
  153. package/src/lib/services/request-manager-state-service/http-manager-state.store.ts +1321 -0
  154. package/src/lib/services/request-manager-state-service/index.ts +3 -0
  155. package/src/lib/services/request-manager-state-service/models/api-request.model.ts +61 -0
  156. package/src/lib/services/request-manager-state-service/models/index.ts +6 -0
  157. package/src/lib/services/request-manager-state-service/models/request-options.model.ts +22 -0
  158. package/src/lib/services/request-manager-state-service/models/stream-type.enum.ts +13 -0
  159. package/src/lib/services/request-manager-state-service/models/ws-options.model.ts +39 -0
  160. package/src/lib/services/store-state-manager-service/index.ts +3 -0
  161. package/src/lib/services/store-state-manager-service/models/index.ts +2 -0
  162. package/src/lib/services/store-state-manager-service/models/state-storage-options.model.ts +24 -0
  163. package/src/lib/services/store-state-manager-service/store-state-manager.service.ts +88 -0
  164. package/src/lib/services/utils/app.service.spec.ts +25 -0
  165. package/src/lib/services/utils/app.service.ts +21 -0
  166. package/src/lib/services/utils/encryption/README.md +79 -0
  167. package/src/lib/services/utils/encryption/asymmetrical-encryption.service.ts +282 -0
  168. package/src/lib/services/utils/encryption/encryption-test.service.ts +39 -0
  169. package/src/lib/services/utils/encryption/index.ts +5 -0
  170. package/src/lib/services/utils/encryption/random.ts +81 -0
  171. package/src/lib/services/utils/encryption/symmetrical-encryption.service.ts +93 -0
  172. package/src/lib/services/utils/headers.service.spec.ts +80 -0
  173. package/src/lib/services/utils/headers.service.ts +18 -0
  174. package/src/lib/services/utils/index.ts +7 -0
  175. package/src/lib/services/utils/object-merger.service.spec.ts +18 -0
  176. package/src/lib/services/utils/object-merger.service.ts +78 -0
  177. package/src/lib/services/utils/path-query.service.spec.ts +117 -0
  178. package/src/lib/services/utils/path-query.service.ts +69 -0
  179. package/src/lib/services/utils/random-color.utils.ts +83 -0
  180. package/src/lib/services/utils/utils.service.spec.ts +165 -0
  181. package/src/lib/services/utils/utils.service.ts +192 -0
  182. package/src/lib/services/ws-manager-service/index.ts +4 -0
  183. package/src/lib/services/ws-manager-service/models/channel-info.model.ts +24 -0
  184. package/src/lib/services/ws-manager-service/models/channel-message-data.model.ts +24 -0
  185. package/src/lib/services/ws-manager-service/models/channel-message.model.ts +24 -0
  186. package/src/lib/services/ws-manager-service/models/communication-type.enum.ts +5 -0
  187. package/src/lib/services/ws-manager-service/models/index.ts +5 -0
  188. package/src/lib/services/ws-manager-service/models/ws-user.model.ts +38 -0
  189. package/src/lib/services/ws-manager-service/services/index.ts +3 -0
  190. package/src/lib/services/ws-manager-service/services/websocket.service.ts +392 -0
  191. package/src/public-api.ts +14 -0
  192. package/tsconfig.lib.json +32 -0
  193. package/tsconfig.lib.prod.json +10 -0
  194. package/tsconfig.spec.json +14 -0
  195. package/fesm2022/http-request-manager.mjs +0 -7634
  196. package/fesm2022/http-request-manager.mjs.map +0 -1
  197. package/http-request-manager-18.7.19.tgz +0 -0
  198. package/types/http-request-manager.d.ts +0 -2278
@@ -0,0 +1,232 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
3
+ import { HTTPManagerService } from './http-manager.service';
4
+ import { ApiRequest } from '../request-manager-state-service/models/api-request.model';
5
+ import { CONFIG_SETTINGS_TOKEN } from '../../models/config-token.model';
6
+ import { ObjectMergerService } from '../utils/object-merger.service';
7
+ import { HttpErrorResponse, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
8
+
9
+ import { ToastMessageDisplayService } from 'toast-message-display';
10
+
11
+
12
+ interface TestData {
13
+ id?: string | number;
14
+ name: string;
15
+ }
16
+
17
+ describe('HTTPManagerService', () => {
18
+ let service: HTTPManagerService<TestData>;
19
+ let httpMock: HttpTestingController;
20
+ let toastMessageServiceSpy: jasmine.SpyObj<ToastMessageDisplayService>;
21
+ let objectMergerServiceSpy: jasmine.SpyObj<ObjectMergerService>;
22
+
23
+ const mockConfig = {
24
+ httpRequestOptions: {
25
+ server: 'http://test-api.com',
26
+ path: [],
27
+ headers: {}
28
+ }
29
+ };
30
+
31
+ beforeEach(() => {
32
+ toastMessageServiceSpy = jasmine.createSpyObj('ToastMessageService', ['toastMessage']);
33
+ objectMergerServiceSpy = jasmine.createSpyObj('ObjectMergerService', ['mergeOptions']);
34
+ objectMergerServiceSpy.mergeOptions.and.callFake((options?: ApiRequest) => {
35
+ return options || ApiRequest.adapt();
36
+ });
37
+
38
+ TestBed.configureTestingModule({
39
+ imports: [],
40
+ providers: [
41
+ HTTPManagerService,
42
+ { provide: ToastMessageDisplayService, useValue: toastMessageServiceSpy },
43
+ { provide: ObjectMergerService, useValue: objectMergerServiceSpy },
44
+ { provide: CONFIG_SETTINGS_TOKEN, useValue: mockConfig },
45
+ provideHttpClient(withInterceptorsFromDi()),
46
+ provideHttpClientTesting()
47
+ ]
48
+ });
49
+
50
+ service = TestBed.inject(HTTPManagerService);
51
+ httpMock = TestBed.inject(HttpTestingController);
52
+ });
53
+
54
+ afterEach(() => {
55
+ httpMock.verify();
56
+ });
57
+
58
+ it('should be created', () => {
59
+ expect(service).toBeTruthy();
60
+ });
61
+
62
+ describe('getRequest', () => {
63
+ it('should make a GET request and handle success', (done) => {
64
+ const testData: TestData = { id: 1, name: 'Test' };
65
+ const options = ApiRequest.adapt({
66
+ server: 'http://test-api.com',
67
+ path: ['test'],
68
+ displayError: true
69
+ });
70
+
71
+ service.getRequest<TestData>(options).subscribe({
72
+ next: (response) => {
73
+ expect(response).toEqual(testData);
74
+ done();
75
+ }
76
+ });
77
+
78
+ const req = httpMock.expectOne('http://test-api.com/test');
79
+ expect(req.request.method).toBe('GET');
80
+ req.flush(testData);
81
+ });
82
+ });
83
+
84
+ describe('postRequest', () => {
85
+ it('should make a POST request and handle success', (done) => {
86
+ const testData: TestData = { id: undefined, name: 'Test' };
87
+ const responseData: TestData = { id: 1, name: 'Test' };
88
+ const options = ApiRequest.adapt({
89
+ server: 'http://test-api.com',
90
+ path: ['test']
91
+ });
92
+
93
+ service.postRequest<TestData>(testData, options).subscribe({
94
+ next: (response) => {
95
+ expect(response).toEqual(responseData);
96
+ done();
97
+ }
98
+ });
99
+
100
+ const req = httpMock.expectOne('http://test-api.com/test');
101
+ expect(req.request.method).toBe('POST');
102
+ expect(req.request.body).toEqual(testData);
103
+ req.flush(responseData);
104
+ });
105
+ });
106
+
107
+ describe('putRequest', () => {
108
+ it('should make a PUT request and handle success', (done) => {
109
+ const testData: TestData = { id: 1, name: 'Updated Test' };
110
+ const options = ApiRequest.adapt({
111
+ server: 'http://test-api.com',
112
+ path: ['test', 1]
113
+ });
114
+
115
+ service.putRequest<TestData>(testData, options).subscribe({
116
+ next: (response) => {
117
+ expect(response).toEqual(testData);
118
+ done();
119
+ }
120
+ });
121
+
122
+ const req = httpMock.expectOne('http://test-api.com/test/1');
123
+ expect(req.request.method).toBe('PUT');
124
+ expect(req.request.body).toEqual(testData);
125
+ req.flush(testData);
126
+ });
127
+ });
128
+
129
+ describe('deleteRequest', () => {
130
+ it('should make a DELETE request and handle success', (done) => {
131
+ const options = ApiRequest.adapt({
132
+ server: 'http://test-api.com',
133
+ path: ['test', 1]
134
+ });
135
+
136
+ service.deleteRequest(options).subscribe({
137
+ next: (response) => {
138
+ expect(response).toBeNull();
139
+ done();
140
+ }
141
+ });
142
+
143
+ const req = httpMock.expectOne('http://test-api.com/test/1');
144
+ expect(req.request.method).toBe('DELETE');
145
+ req.flush(null);
146
+ });
147
+ });
148
+
149
+ describe('Polling', () => {
150
+ let clock: jasmine.Clock;
151
+
152
+ beforeEach(() => {
153
+ clock = jasmine.clock();
154
+ clock.install();
155
+ });
156
+
157
+ afterEach(() => {
158
+ clock.uninstall();
159
+ });
160
+
161
+ it('should handle polling requests', (done) => {
162
+ const testData: TestData = { id: 1, name: 'Test' };
163
+ const options = ApiRequest.adapt({
164
+ server: 'http://test-api.com',
165
+ path: ['test'],
166
+ polling: 5
167
+ });
168
+
169
+ let callCount = 0;
170
+
171
+ // Force synchronous execution of observables
172
+ jasmine.clock().mockDate(new Date());
173
+
174
+ const subscription = service.getRequest<TestData>(options).subscribe({
175
+ next: (response) => {
176
+ expect(response).toEqual(testData);
177
+ callCount++;
178
+
179
+ if (callCount === 1) {
180
+ // Trigger the next polling cycle
181
+ clock.tick(5000);
182
+ }
183
+
184
+ if (callCount === 2) {
185
+ subscription.unsubscribe();
186
+ service['stopPolling']();
187
+ done();
188
+ }
189
+ },
190
+ error: (error) => {
191
+ subscription.unsubscribe();
192
+ done.fail(error);
193
+ }
194
+ });
195
+
196
+ // Handle first request
197
+ const req1 = httpMock.expectOne('http://test-api.com/test');
198
+ expect(req1.request.method).toBe('GET');
199
+ req1.flush(testData);
200
+
201
+ // Wait for the next polling cycle
202
+ clock.tick(5000);
203
+
204
+ // Handle second request
205
+ const req2 = httpMock.expectOne('http://test-api.com/test');
206
+ expect(req2.request.method).toBe('GET');
207
+ req2.flush(testData);
208
+ });
209
+ });
210
+
211
+ describe('Error Handling', () => {
212
+ it('should handle network errors', (done) => {
213
+ const options = ApiRequest.adapt({
214
+ server: 'http://test-api.com',
215
+ path: ['test'],
216
+ displayError: true
217
+ });
218
+
219
+ service.getRequest<TestData>(options).subscribe({
220
+ error: (error) => {
221
+ expect(error instanceof HttpErrorResponse).toBeTrue();
222
+ expect(service.isPending.getValue()).toBeFalse();
223
+ expect(toastMessageServiceSpy.toastMessage).toHaveBeenCalled();
224
+ done();
225
+ }
226
+ });
227
+
228
+ const req = httpMock.expectOne('http://test-api.com/test');
229
+ req.error(new ErrorEvent('Network error'));
230
+ });
231
+ });
232
+ });
@@ -0,0 +1,274 @@
1
+ import { Inject, inject, Injectable, Injector, Optional } from '@angular/core';
2
+ import { HttpErrorResponse } from '@angular/common/http';
3
+ import { BehaviorSubject, Observable, Subject, throwError } from 'rxjs';
4
+ import { tap, map, finalize, catchError } from 'rxjs/operators';
5
+
6
+ import { ApiRequest } from '../request-manager-state-service/models/api-request.model';
7
+ import { RequestService } from './request.service';
8
+ import { requestPolling, countdown, delayedRetry } from './rxjs-operators';
9
+
10
+
11
+ import { CONFIG_SETTINGS_TOKEN } from '../../models/config-token.model';
12
+ import { ConfigOptions } from '../../models';
13
+ import { ObjectMergerService } from '../utils/object-merger.service';
14
+ import { ToastColors, ToastDisplay, ToastMessageDisplayService } from 'toast-message-display';
15
+
16
+ @Injectable({
17
+ providedIn: 'root',
18
+ })
19
+ export class HTTPManagerService<T> extends RequestService {
20
+
21
+ toastMessage = inject(ToastMessageDisplayService)
22
+ ng_injector = inject(Injector)
23
+ objectMergerService = inject(ObjectMergerService)
24
+
25
+ private countdown = new BehaviorSubject<number>(0);
26
+ countdown$ = this.countdown.asObservable();
27
+
28
+ private error = new BehaviorSubject<boolean>(false);
29
+ error$ = this.error.asObservable();
30
+
31
+ private data = new BehaviorSubject<any>(null)
32
+ data$ = this.data.asObservable()
33
+
34
+ private polling$ = new Subject<void>();
35
+
36
+ config = ApiRequest.adapt();
37
+
38
+ constructor(@Optional() @Inject(CONFIG_SETTINGS_TOKEN) private configOptions?: ConfigOptions) {
39
+ super()
40
+ this.config = (configOptions) ? ApiRequest.adapt(configOptions.httpRequestOptions) : this.config
41
+ }
42
+
43
+ // REQUESTS
44
+
45
+ getRequest<T>(options?: ApiRequest, params?: any[]) {
46
+
47
+ this.isPending.next(true);
48
+ this.data.next(null)
49
+
50
+ const updatedOptions = this.defineReqOptions(options, params)
51
+ const func = this.getRecordRequest;
52
+ const requests = this.createRequest(func, updatedOptions);
53
+
54
+ return this.createObservable<T>(updatedOptions, requests, func.name)
55
+ .pipe(
56
+ tap(data => this.data.next(data))
57
+ ).pipe(
58
+ finalize(() => this.isPending.next(false)),
59
+ catchError((err) => {
60
+ if(updatedOptions.displayError) this.handleErrorWithSnackBar(err)
61
+ this.isPending.next(false)
62
+ return this.handleError(err);
63
+ })
64
+ )
65
+ }
66
+
67
+ postRequest<T extends { id?: number|string }>(data: T, options: ApiRequest, params?: any[]) {
68
+
69
+ this.isPending.next(true);
70
+ this.data.next(null)
71
+
72
+ const updatedOptions = this.defineReqOptions(options, params)
73
+ const func = this.createRecordRequest;
74
+ const requests = this.createRequest(func, updatedOptions, data);
75
+
76
+ return this.createObservable<T>(updatedOptions, requests, func.name).pipe(tap(data => this.data.next(data)))
77
+ .pipe(
78
+ finalize(() => this.isPending.next(false)),
79
+ catchError((err) => {
80
+ if(updatedOptions.displayError) this.handleErrorWithSnackBar(err)
81
+ this.isPending.next(false)
82
+ return this.handleError(err);
83
+ })
84
+ )
85
+ }
86
+
87
+ putRequest<T extends { id?: number|string }>(data: T, options: ApiRequest, params?: any[]) {
88
+
89
+ this.isPending.next(true);
90
+ this.data.next(null)
91
+
92
+ const updatedOptions = this.defineReqOptions(options, params)
93
+ const func = this.updateRecordRequest;
94
+ const requests = this.createRequest(func, updatedOptions, data);
95
+
96
+ return this.createObservable<T>(updatedOptions, requests, func.name).pipe(tap(data => this.data.next(data)))
97
+ .pipe(
98
+ finalize(() => this.isPending.next(false)),
99
+ catchError((err) => {
100
+ if(updatedOptions.displayError) this.handleErrorWithSnackBar(err)
101
+ this.isPending.next(false)
102
+ return this.handleError(err);
103
+ })
104
+ )
105
+ }
106
+
107
+ deleteRequest<T>(options: ApiRequest, params?: any[]) {
108
+
109
+ this.isPending.next(true);
110
+ this.data.next(null)
111
+
112
+ const updatedOptions = this.defineReqOptions(options, params)
113
+ const func = this.deleteRecordRequest;
114
+ const requests = this.createRequest(func, updatedOptions);
115
+
116
+ return this.createObservable<T>(updatedOptions, requests, func.name).pipe(tap(data => this.data.next(data)))
117
+ .pipe(
118
+ finalize(() => this.isPending.next(false)),
119
+ catchError((err) => {
120
+ if(updatedOptions.displayError) this.handleErrorWithSnackBar(err)
121
+ this.isPending.next(false)
122
+ return this.handleError(err);
123
+ })
124
+ )
125
+ }
126
+
127
+ downloadRequest<T>(options: ApiRequest, params?: any[], saveAs?: string) {
128
+
129
+ this.isPending.next(true);
130
+
131
+ const updatedOptions = this.defineReqOptions(options, params)
132
+ const func = this.downloadFileRequest;
133
+ const requests = this.createRequest(func, updatedOptions)
134
+
135
+ return this.createObservable<T>(updatedOptions, requests, func.name)
136
+ .pipe(
137
+ catchError((err) => {
138
+ this.error.next(true)
139
+ this.isPending.next(false)
140
+ return this.handleError(err);
141
+ })
142
+ )
143
+
144
+ }
145
+
146
+ private createObservable<T>(options: ApiRequest, request$: Observable<T>, funcName: string) {
147
+
148
+ const polling = options.polling ? (options.polling > 0 ? true : false) : false;
149
+ const isPolling =
150
+ polling &&
151
+ !(funcName === 'deleteRecordRequest' || funcName === 'updateRecordRequest' || funcName === 'createRecordRequest')
152
+ ? true
153
+ : false;
154
+
155
+ this.polling$.next();
156
+
157
+ const polling$ =
158
+ (isPolling && options.polling) || 0 >= 3
159
+ ? request$.pipe(
160
+ requestPolling((options.polling || 0) + 1, this.polling$, this.isPending),
161
+ tap(() => this.countdown.next(0)),
162
+ tap(() => {
163
+ if (!options.polling) return;
164
+ const count = options.polling ? options.polling : 0;
165
+ // console.log('COUNT:', count);
166
+ countdown(count)
167
+ .pipe(
168
+ map((x: number) => {
169
+ // console.log('XX:', count, x);
170
+ const pollingInSec = options.polling || 0;
171
+ const percentageCompleted = ((pollingInSec - x) / pollingInSec) * 100;
172
+ return Math.round(percentageCompleted);
173
+ })
174
+ )
175
+ .subscribe((countDownValue: number) => {
176
+ // console.log(this.countdown.value, countDownValue);
177
+ this.countdown.next(countDownValue);
178
+ });
179
+ }),
180
+ )
181
+ : request$.pipe(
182
+ catchError((err: any) => {
183
+
184
+ if (err instanceof HttpErrorResponse) {
185
+ this.error.next(true);
186
+ return this.handleError(err);
187
+ }
188
+
189
+ return throwError(() => err);
190
+ }),
191
+ // finalize(() => this.isPending.next(false))
192
+ )
193
+
194
+ return polling$.pipe(
195
+ catchError((err: any, caught: Observable<T>) => {
196
+
197
+ if (err instanceof HttpErrorResponse) {
198
+ this.error.next(true);
199
+ if(isPolling) this.stopPolling();
200
+ return this.handleError(err);
201
+ }
202
+
203
+ return throwError(() => err);
204
+ }),
205
+ (options?.retry && options.retry.times > 0)
206
+ ? delayedRetry((options.retry.delay || 3) * 1000, (options.retry.times || 0) - 1)
207
+ : (source: Observable<T>) => source
208
+ );
209
+ }
210
+
211
+ private createRequest(func: Function, options: ApiRequest, data?: any | any[]) {
212
+ const dataItem = this.prepareRequestData(options, data, func.name);
213
+ return func.bind(this)(dataItem.options, dataItem.data);
214
+ }
215
+
216
+ private prepareRequestData(options: ApiRequest, data: any, funcName: string) {
217
+
218
+ if ((options.mapper && funcName === 'updateRecordRequest') || funcName === 'createRecordRequest') {
219
+ if (options?.mapper) {
220
+ data = options.mapper
221
+ ? Array.isArray(data)
222
+ ? map((item) => options.mapper(item))
223
+ : options.mapper(data)
224
+ : data;
225
+ }
226
+ } else {
227
+ if (options?.adapter) {
228
+ data = Array.isArray(data)
229
+ ? map((item) => options.adapter(item))
230
+ : options.adapter(data)
231
+ }
232
+ }
233
+
234
+ return { options: options, data: data };
235
+ }
236
+
237
+ // MISC
238
+
239
+ private handleError(error: Response | any) {
240
+ this.error.next(error.message || `${error.status} - ${error.statusText}`)
241
+ return throwError(() => error)
242
+ }
243
+
244
+ private handleErrorWithSnackBar(error: Response | any) {
245
+
246
+ const displayError = ToastDisplay.adapt({
247
+ message: error.message || `${error.status} - ${error.statusText}`,
248
+ action: 'OK',
249
+ color: ToastColors.ERROR,
250
+ icon: 'error',
251
+ duration: 5 * 1000, //5 seconds
252
+ })
253
+
254
+ this.toastMessage.toastMessage(displayError)
255
+
256
+ }
257
+
258
+ private stopPolling() {
259
+ this.isPending.next(false);
260
+ this.polling$.next();
261
+ }
262
+
263
+ private defineReqOptions(options: any, params?: any[]) {
264
+
265
+ const req = ApiRequest.adapt(options)
266
+ if(req?.path) req.path = (params) ? [...req.path, ...params] : req.path
267
+
268
+ const optionsReq = (req)? req : ApiRequest.adapt()
269
+ const updatedOptions = this.objectMergerService.mergeOptions(optionsReq)
270
+
271
+ return updatedOptions
272
+
273
+ }
274
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./http-manager.service";
2
+ export * from "./http-manager-signals.service";
3
+
4
+ export * from './request.service';
5
+ export * from './request-signals.service';
6
+
7
+ export * from "../../models/index";
8
+ export * from "./rxjs-operators/index";