@c8y/ngx-components 1021.66.0 → 1021.70.1

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 (207) hide show
  1. package/core/common/bytes.pipe.d.ts +26 -10
  2. package/core/common/bytes.pipe.d.ts.map +1 -1
  3. package/echart/charts.component.d.ts +14 -3
  4. package/echart/charts.component.d.ts.map +1 -1
  5. package/esm2022/core/common/bytes.pipe.mjs +47 -18
  6. package/esm2022/core/data-grid/data-grid.component.mjs +3 -3
  7. package/esm2022/echart/charts.component.mjs +50 -3
  8. package/esm2022/messaging-management/api/model/backlogQuota.mjs +2 -0
  9. package/esm2022/messaging-management/api/model/namespace.mjs +2 -0
  10. package/esm2022/messaging-management/api/model/namespaceDetails.mjs +2 -0
  11. package/esm2022/messaging-management/api/model/namespaceList.mjs +2 -0
  12. package/esm2022/messaging-management/api/model/namespacePolicies.mjs +2 -0
  13. package/esm2022/messaging-management/api/model/namespacePublishers.mjs +2 -0
  14. package/esm2022/messaging-management/api/model/namespaceSubscribers.mjs +2 -0
  15. package/esm2022/messaging-management/api/model/namespaceTopics.mjs +2 -0
  16. package/esm2022/messaging-management/api/model/pageStatistics.mjs +2 -0
  17. package/esm2022/messaging-management/api/model/pageable.mjs +2 -0
  18. package/esm2022/messaging-management/api/model/sortable.mjs +2 -0
  19. package/esm2022/messaging-management/api/model/subscriber.mjs +2 -0
  20. package/esm2022/messaging-management/api/model/subscriberFilters.mjs +2 -0
  21. package/esm2022/messaging-management/api/model/subscriberList.mjs +2 -0
  22. package/esm2022/messaging-management/api/model/subscriberToDelete.mjs +2 -0
  23. package/esm2022/messaging-management/api/model/topic.mjs +2 -0
  24. package/esm2022/messaging-management/api/model/topicDetailFilters.mjs +2 -0
  25. package/esm2022/messaging-management/api/model/topicList.mjs +2 -0
  26. package/esm2022/messaging-management/api/model/topicListFilters.mjs +2 -0
  27. package/esm2022/messaging-management/api/model/topicType.mjs +9 -0
  28. package/esm2022/messaging-management/api/services/messaging-namespaces.service.mjs +83 -0
  29. package/esm2022/messaging-management/api/services/messaging-subscribers.service.mjs +55 -0
  30. package/esm2022/messaging-management/api/services/messaging-topics.service.mjs +48 -0
  31. package/esm2022/messaging-management/c8y-ngx-components-messaging-management.mjs +5 -0
  32. package/esm2022/messaging-management/constants.mjs +4 -0
  33. package/esm2022/messaging-management/index.mjs +2 -0
  34. package/esm2022/messaging-management/messaging/namespace-list/namespace-item/namespace-item-card/namespace-item-card.component.mjs +43 -0
  35. package/esm2022/messaging-management/messaging/namespace-list/namespace-item/namespace-item.component.mjs +36 -0
  36. package/esm2022/messaging-management/messaging/namespace-list/namespace-list.component.mjs +51 -0
  37. package/esm2022/messaging-management/messaging/shared/usage/usage.component.mjs +68 -0
  38. package/esm2022/messaging-management/messaging/topic/topic-list-view.component.mjs +81 -0
  39. package/esm2022/messaging-management/messaging/topic/topic-subscribers-view/topic-subscribers-data-grid.service.mjs +220 -0
  40. package/esm2022/messaging-management/messaging/topic/topic-subscribers-view/topic-subscribers-view.component.mjs +137 -0
  41. package/esm2022/messaging-management/messaging/topic/topics-data-grid.service.mjs +113 -0
  42. package/esm2022/messaging-management/messaging-management.guard.mjs +40 -0
  43. package/esm2022/messaging-management/messaging-management.module.mjs +72 -0
  44. package/esm2022/messaging-management/navigator/messaging-navigator-factory.mjs +55 -0
  45. package/esm2022/messaging-management/navigator/topic-details-tab.factory.mjs +33 -0
  46. package/esm2022/messaging-management/utils/backlog-quota-limit.pipe.mjs +32 -0
  47. package/esm2022/messaging-management/utils/namespace-props.mjs +23 -0
  48. package/esm2022/messaging-management/utils/time-to-live.pipe.mjs +122 -0
  49. package/esm2022/operations/device-selector/device-selector.component.mjs +5 -1
  50. package/esm2022/widgets/cockpit/index.mjs +3 -1
  51. package/esm2022/widgets/cockpit-exports/index.mjs +8 -1
  52. package/esm2022/widgets/definitions/index.mjs +2 -1
  53. package/esm2022/widgets/definitions/radial-gauge/c8y-ngx-components-widgets-definitions-radial-gauge.mjs +5 -0
  54. package/esm2022/widgets/definitions/radial-gauge/index.mjs +33 -0
  55. package/esm2022/widgets/implementations/datapoints-graph/datapoints-graph-view/datapoints-graph-widget-view.component.mjs +18 -3
  56. package/esm2022/widgets/implementations/info-gauge/index.mjs +2 -2
  57. package/esm2022/widgets/implementations/info-gauge/info-gauge-widget-config/gauge.model.mjs +295 -0
  58. package/esm2022/widgets/implementations/info-gauge/info-gauge-widget-config/info-gauge-widget-config.component.mjs +141 -11
  59. package/esm2022/widgets/implementations/info-gauge/info-gauge-widget-config/preset-preview/preset-preview.component.mjs +67 -0
  60. package/esm2022/widgets/implementations/info-gauge/info-gauge-widget-view/info-gauge-widget-view.component.mjs +34 -6
  61. package/esm2022/widgets/implementations/info-gauge/info-gauge-widget.module.mjs +9 -4
  62. package/esm2022/widgets/implementations/info-gauge/radial-gauge/radial-gauge.component.mjs +97 -0
  63. package/esm2022/widgets/implementations/info-gauge/radial-gauge/radial-gauge.service.mjs +369 -0
  64. package/fesm2022/c8y-ngx-components-echart.mjs +49 -2
  65. package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
  66. package/fesm2022/c8y-ngx-components-messaging-management.mjs +1225 -0
  67. package/fesm2022/c8y-ngx-components-messaging-management.mjs.map +1 -0
  68. package/fesm2022/c8y-ngx-components-operations-device-selector.mjs +4 -0
  69. package/fesm2022/c8y-ngx-components-operations-device-selector.mjs.map +1 -1
  70. package/fesm2022/c8y-ngx-components-widgets-cockpit-exports.mjs +7 -0
  71. package/fesm2022/c8y-ngx-components-widgets-cockpit-exports.mjs.map +1 -1
  72. package/fesm2022/c8y-ngx-components-widgets-cockpit.mjs +2 -0
  73. package/fesm2022/c8y-ngx-components-widgets-cockpit.mjs.map +1 -1
  74. package/fesm2022/c8y-ngx-components-widgets-definitions-radial-gauge.mjs +40 -0
  75. package/fesm2022/c8y-ngx-components-widgets-definitions-radial-gauge.mjs.map +1 -0
  76. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs +1 -0
  77. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs.map +1 -1
  78. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs +17 -2
  79. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-graph.mjs.map +1 -1
  80. package/fesm2022/c8y-ngx-components-widgets-implementations-info-gauge.mjs +991 -28
  81. package/fesm2022/c8y-ngx-components-widgets-implementations-info-gauge.mjs.map +1 -1
  82. package/fesm2022/c8y-ngx-components.mjs +49 -22
  83. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  84. package/locales/de.po +272 -5
  85. package/locales/es.po +271 -4
  86. package/locales/fr.po +272 -5
  87. package/locales/ja_JP.po +272 -5
  88. package/locales/ko.po +271 -4
  89. package/locales/locales.pot +271 -4
  90. package/locales/nl.po +271 -4
  91. package/locales/pl.po +271 -4
  92. package/locales/pt_BR.po +272 -5
  93. package/locales/zh_CN.po +271 -4
  94. package/locales/zh_TW.po +272 -5
  95. package/messaging-management/api/model/backlogQuota.d.ts +14 -0
  96. package/messaging-management/api/model/backlogQuota.d.ts.map +1 -0
  97. package/messaging-management/api/model/namespace.d.ts +13 -0
  98. package/messaging-management/api/model/namespace.d.ts.map +1 -0
  99. package/messaging-management/api/model/namespaceDetails.d.ts +8 -0
  100. package/messaging-management/api/model/namespaceDetails.d.ts.map +1 -0
  101. package/messaging-management/api/model/namespaceList.d.ts +5 -0
  102. package/messaging-management/api/model/namespaceList.d.ts.map +1 -0
  103. package/messaging-management/api/model/namespacePolicies.d.ts +10 -0
  104. package/messaging-management/api/model/namespacePolicies.d.ts.map +1 -0
  105. package/messaging-management/api/model/namespacePublishers.d.ts +4 -0
  106. package/messaging-management/api/model/namespacePublishers.d.ts.map +1 -0
  107. package/messaging-management/api/model/namespaceSubscribers.d.ts +4 -0
  108. package/messaging-management/api/model/namespaceSubscribers.d.ts.map +1 -0
  109. package/messaging-management/api/model/namespaceTopics.d.ts +5 -0
  110. package/messaging-management/api/model/namespaceTopics.d.ts.map +1 -0
  111. package/messaging-management/api/model/pageStatistics.d.ts +12 -0
  112. package/messaging-management/api/model/pageStatistics.d.ts.map +1 -0
  113. package/messaging-management/api/model/pageable.d.ts +8 -0
  114. package/messaging-management/api/model/pageable.d.ts.map +1 -0
  115. package/messaging-management/api/model/sortable.d.ts +6 -0
  116. package/messaging-management/api/model/sortable.d.ts.map +1 -0
  117. package/messaging-management/api/model/subscriber.d.ts +28 -0
  118. package/messaging-management/api/model/subscriber.d.ts.map +1 -0
  119. package/messaging-management/api/model/subscriberFilters.d.ts +13 -0
  120. package/messaging-management/api/model/subscriberFilters.d.ts.map +1 -0
  121. package/messaging-management/api/model/subscriberList.d.ts +7 -0
  122. package/messaging-management/api/model/subscriberList.d.ts.map +1 -0
  123. package/messaging-management/api/model/subscriberToDelete.d.ts +12 -0
  124. package/messaging-management/api/model/subscriberToDelete.d.ts.map +1 -0
  125. package/messaging-management/api/model/topic.d.ts +52 -0
  126. package/messaging-management/api/model/topic.d.ts.map +1 -0
  127. package/messaging-management/api/model/topicDetailFilters.d.ts +9 -0
  128. package/messaging-management/api/model/topicDetailFilters.d.ts.map +1 -0
  129. package/messaging-management/api/model/topicList.d.ts +7 -0
  130. package/messaging-management/api/model/topicList.d.ts.map +1 -0
  131. package/messaging-management/api/model/topicListFilters.d.ts +12 -0
  132. package/messaging-management/api/model/topicListFilters.d.ts.map +1 -0
  133. package/messaging-management/api/model/topicType.d.ts +8 -0
  134. package/messaging-management/api/model/topicType.d.ts.map +1 -0
  135. package/messaging-management/api/services/messaging-namespaces.service.d.ts +52 -0
  136. package/messaging-management/api/services/messaging-namespaces.service.d.ts.map +1 -0
  137. package/messaging-management/api/services/messaging-subscribers.service.d.ts +30 -0
  138. package/messaging-management/api/services/messaging-subscribers.service.d.ts.map +1 -0
  139. package/messaging-management/api/services/messaging-topics.service.d.ts +21 -0
  140. package/messaging-management/api/services/messaging-topics.service.d.ts.map +1 -0
  141. package/messaging-management/c8y-ngx-components-messaging-management.d.ts.map +1 -0
  142. package/messaging-management/constants.d.ts +4 -0
  143. package/messaging-management/constants.d.ts.map +1 -0
  144. package/messaging-management/index.d.ts +2 -0
  145. package/messaging-management/index.d.ts.map +1 -0
  146. package/messaging-management/messaging/namespace-list/namespace-item/namespace-item-card/namespace-item-card.component.d.ts +29 -0
  147. package/messaging-management/messaging/namespace-list/namespace-item/namespace-item-card/namespace-item-card.component.d.ts.map +1 -0
  148. package/messaging-management/messaging/namespace-list/namespace-item/namespace-item.component.d.ts +15 -0
  149. package/messaging-management/messaging/namespace-list/namespace-item/namespace-item.component.d.ts.map +1 -0
  150. package/messaging-management/messaging/namespace-list/namespace-list.component.d.ts +17 -0
  151. package/messaging-management/messaging/namespace-list/namespace-list.component.d.ts.map +1 -0
  152. package/messaging-management/messaging/shared/usage/usage.component.d.ts +40 -0
  153. package/messaging-management/messaging/shared/usage/usage.component.d.ts.map +1 -0
  154. package/messaging-management/messaging/topic/topic-list-view.component.d.ts +38 -0
  155. package/messaging-management/messaging/topic/topic-list-view.component.d.ts.map +1 -0
  156. package/messaging-management/messaging/topic/topic-subscribers-view/topic-subscribers-data-grid.service.d.ts +21 -0
  157. package/messaging-management/messaging/topic/topic-subscribers-view/topic-subscribers-data-grid.service.d.ts.map +1 -0
  158. package/messaging-management/messaging/topic/topic-subscribers-view/topic-subscribers-view.component.d.ts +52 -0
  159. package/messaging-management/messaging/topic/topic-subscribers-view/topic-subscribers-view.component.d.ts.map +1 -0
  160. package/messaging-management/messaging/topic/topics-data-grid.service.d.ts +13 -0
  161. package/messaging-management/messaging/topic/topics-data-grid.service.d.ts.map +1 -0
  162. package/messaging-management/messaging-management.guard.d.ts +12 -0
  163. package/messaging-management/messaging-management.guard.d.ts.map +1 -0
  164. package/messaging-management/messaging-management.module.d.ts +7 -0
  165. package/messaging-management/messaging-management.module.d.ts.map +1 -0
  166. package/messaging-management/navigator/messaging-navigator-factory.d.ts +18 -0
  167. package/messaging-management/navigator/messaging-navigator-factory.d.ts.map +1 -0
  168. package/messaging-management/navigator/topic-details-tab.factory.d.ts +15 -0
  169. package/messaging-management/navigator/topic-details-tab.factory.d.ts.map +1 -0
  170. package/messaging-management/utils/backlog-quota-limit.pipe.d.ts +13 -0
  171. package/messaging-management/utils/backlog-quota-limit.pipe.d.ts.map +1 -0
  172. package/messaging-management/utils/namespace-props.d.ts +10 -0
  173. package/messaging-management/utils/namespace-props.d.ts.map +1 -0
  174. package/messaging-management/utils/time-to-live.pipe.d.ts +16 -0
  175. package/messaging-management/utils/time-to-live.pipe.d.ts.map +1 -0
  176. package/operations/device-selector/device-selector.component.d.ts.map +1 -1
  177. package/package.json +1 -1
  178. package/widgets/cockpit/index.d.ts +29 -0
  179. package/widgets/cockpit/index.d.ts.map +1 -1
  180. package/widgets/cockpit-exports/index.d.ts +6 -0
  181. package/widgets/cockpit-exports/index.d.ts.map +1 -1
  182. package/widgets/definitions/index.d.ts +1 -0
  183. package/widgets/definitions/index.d.ts.map +1 -1
  184. package/widgets/definitions/radial-gauge/c8y-ngx-components-widgets-definitions-radial-gauge.d.ts.map +1 -0
  185. package/widgets/definitions/radial-gauge/index.d.ts +33 -0
  186. package/widgets/definitions/radial-gauge/index.d.ts.map +1 -0
  187. package/widgets/implementations/datapoints-graph/datapoints-graph-view/datapoints-graph-widget-view.component.d.ts +2 -0
  188. package/widgets/implementations/datapoints-graph/datapoints-graph-view/datapoints-graph-widget-view.component.d.ts.map +1 -1
  189. package/widgets/implementations/info-gauge/index.d.ts +1 -1
  190. package/widgets/implementations/info-gauge/index.d.ts.map +1 -1
  191. package/widgets/implementations/info-gauge/info-gauge-widget-config/gauge.model.d.ts +339 -0
  192. package/widgets/implementations/info-gauge/info-gauge-widget-config/gauge.model.d.ts.map +1 -0
  193. package/widgets/implementations/info-gauge/info-gauge-widget-config/info-gauge-widget-config.component.d.ts +27 -7
  194. package/widgets/implementations/info-gauge/info-gauge-widget-config/info-gauge-widget-config.component.d.ts.map +1 -1
  195. package/widgets/implementations/info-gauge/info-gauge-widget-config/preset-preview/preset-preview.component.d.ts +33 -0
  196. package/widgets/implementations/info-gauge/info-gauge-widget-config/preset-preview/preset-preview.component.d.ts.map +1 -0
  197. package/widgets/implementations/info-gauge/info-gauge-widget-view/info-gauge-widget-view.component.d.ts +7 -3
  198. package/widgets/implementations/info-gauge/info-gauge-widget-view/info-gauge-widget-view.component.d.ts.map +1 -1
  199. package/widgets/implementations/info-gauge/info-gauge-widget.module.d.ts +2 -1
  200. package/widgets/implementations/info-gauge/info-gauge-widget.module.d.ts.map +1 -1
  201. package/widgets/implementations/info-gauge/radial-gauge/radial-gauge.component.d.ts +37 -0
  202. package/widgets/implementations/info-gauge/radial-gauge/radial-gauge.component.d.ts.map +1 -0
  203. package/widgets/implementations/info-gauge/radial-gauge/radial-gauge.service.d.ts +146 -0
  204. package/widgets/implementations/info-gauge/radial-gauge/radial-gauge.service.d.ts.map +1 -0
  205. package/esm2022/widgets/implementations/info-gauge/info-gauge.model.mjs +0 -2
  206. package/widgets/implementations/info-gauge/info-gauge.model.d.ts +0 -7
  207. package/widgets/implementations/info-gauge/info-gauge.model.d.ts.map +0 -1
@@ -0,0 +1,1225 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Injectable, LOCALE_ID, Pipe, input, computed, Component, Input, DestroyRef, EventEmitter, ViewChild, NgModule } from '@angular/core';
3
+ import * as i1$1 from '@c8y/ngx-components';
4
+ import { gettext, AlertService, Permissions, NavigatorNode, AppStateService, NavigatorService, IconDirective, C8yTranslatePipe, C8yTranslateModule, HeaderModule, HelpModule, BreadcrumbModule, ActionBarItemComponent, LoadingComponent, BaseColumn, DataGridModule, BytesPipe, EmptyStateComponent, EmptyStateContextDirective, ModalService, Status, DataGridComponent, C8yTranslateDirective, RelativeTimePipe, hookRoute, hookTab, hookNavigator } from '@c8y/ngx-components';
5
+ import * as i1 from '@c8y/client';
6
+ import { Service, UserService, FeatureService, ApplicationService, Paging } from '@c8y/client';
7
+ import { filter, map, take, switchMap, catchError, shareReplay, tap } from 'rxjs/operators';
8
+ import { from, BehaviorSubject, combineLatest, firstValueFrom } from 'rxjs';
9
+ import * as i2 from '@angular/common';
10
+ import { formatNumber, NgIf, NgClass, PercentPipe, CommonModule, DecimalPipe, AsyncPipe } from '@angular/common';
11
+ import * as i1$3 from '@angular/router';
12
+ import { RouterLink, ActivatedRoute } from '@angular/router';
13
+ import * as i1$2 from '@ngx-translate/core';
14
+ import { TranslateService } from '@ngx-translate/core';
15
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
16
+ import { omit } from 'lodash-es';
17
+
18
+ /**
19
+ * Currently known namespaces and their properties.
20
+ */
21
+ const NAMESPACE_PROPS = {
22
+ mqtt: {
23
+ icon: 'c8y-device-protocols',
24
+ label: gettext('MQTT Service')
25
+ },
26
+ 'data-broker-fwd': {
27
+ icon: 'c8y-data-broker',
28
+ label: gettext('Data Broker')
29
+ },
30
+ relnotif: {
31
+ icon: 'c8y-notification',
32
+ label: gettext('Notifications 2.0')
33
+ },
34
+ 'streaming-analytics': {
35
+ icon: 'c8y-streaming-analytics',
36
+ label: gettext('Streaming Analytics')
37
+ }
38
+ };
39
+
40
+ class MessagingNamespacesService extends Service {
41
+ constructor(client) {
42
+ super(client);
43
+ this.baseUrl = '/service/messaging-management';
44
+ this.listUrl = 'tenants';
45
+ this.alertService = inject(AlertService);
46
+ }
47
+ /**
48
+ * Get namespace list for a tenant
49
+ *
50
+ * @param tenant Tenant id
51
+ * @return Namespaces list
52
+ */
53
+ async getNamespaces(tenant) {
54
+ const namespacesUrl = `/${this.listUrl}/${tenant}/namespaces`;
55
+ return this.fetch(namespacesUrl).then(res => res.json());
56
+ }
57
+ /**
58
+ * Get namespace.
59
+ *
60
+ * @param tenant Tenant ID.
61
+ * @param namespace Name of namespace.
62
+ * @return Namespace.
63
+ */
64
+ async getNamespace(tenant, namespace) {
65
+ const namespaceUrl = `/${this.listUrl}/${tenant}/namespaces/${namespace}`;
66
+ return this.fetch(namespaceUrl).then(res => res.json());
67
+ }
68
+ /**
69
+ * Get namespace policies
70
+ *
71
+ * @param tenant Tenant id
72
+ * @param namespace Name of namespace
73
+ * @return Namespaces policies
74
+ */
75
+ async getNamespacePolicies(tenant, namespace) {
76
+ const policiesUrl = `/${this.listUrl}/${tenant}/namespaces/${namespace}/policies`;
77
+ return this.fetch(policiesUrl).then(res => res.json());
78
+ }
79
+ /**
80
+ * Get single namespace details
81
+ * @param tenant Tenant ID
82
+ * @param namespaceName Namespace name
83
+ * @return Namespace with details
84
+ */
85
+ async getNamespaceDetails(tenant, namespaceName) {
86
+ const namespace = await this.getNamespace(tenant, namespaceName);
87
+ const policies = await this.getNamespacePolicies(tenant, namespaceName);
88
+ return {
89
+ id: namespaceName,
90
+ namespace,
91
+ policies
92
+ };
93
+ }
94
+ /**
95
+ * Get namespaces with details
96
+ *
97
+ * @param tenant Tenant ID
98
+ * @return Namespaces with details
99
+ */
100
+ async getNamespacesDetails(tenant) {
101
+ try {
102
+ const { namespaces } = await this.getNamespaces(tenant);
103
+ return Promise.all(namespaces.map(async (namespace) => this.getNamespaceDetails(tenant, namespace.name)));
104
+ }
105
+ catch (error) {
106
+ this.alertService.addServerFailure(error);
107
+ return [];
108
+ }
109
+ }
110
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingNamespacesService, deps: [{ token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
111
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingNamespacesService, providedIn: 'root' }); }
112
+ }
113
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingNamespacesService, decorators: [{
114
+ type: Injectable,
115
+ args: [{ providedIn: 'root' }]
116
+ }], ctorParameters: () => [{ type: i1.FetchClient }] });
117
+
118
+ const basePath = 'monitoring/messaging-service';
119
+ const MESSAGING_MANAGEMENT_FEATURE_KEY = 'messaging-management.api';
120
+ const MESSAGING_MANAGEMENT_MICROSERVICE_NAME = 'messaging-management';
121
+
122
+ class MessagingManagementGuard {
123
+ constructor() {
124
+ this.userService = inject(UserService);
125
+ this.featureService = inject(FeatureService);
126
+ this.applicationService = inject(ApplicationService);
127
+ this.cachedResult = null;
128
+ }
129
+ async canActivate() {
130
+ if (this.cachedResult !== null) {
131
+ return this.cachedResult;
132
+ }
133
+ const currentUser = (await this.userService.current()).data;
134
+ const hasRequiredRoles = this.userService.hasAnyRole(currentUser, [
135
+ Permissions.ROLE_TENANT_STATISTICS_READ,
136
+ Permissions.ROLE_TENANT_MANAGEMENT_ADMIN
137
+ ]);
138
+ const featureEnabled = await this.featureService
139
+ .detail(MESSAGING_MANAGEMENT_FEATURE_KEY)
140
+ .then(({ data }) => data.active)
141
+ .catch(() => false);
142
+ const microserviceSubscribed = await this.applicationService
143
+ .isAvailable(MESSAGING_MANAGEMENT_MICROSERVICE_NAME)
144
+ .then(({ data: subscribed }) => subscribed)
145
+ .catch(() => false);
146
+ this.cachedResult = hasRequiredRoles && featureEnabled && microserviceSubscribed;
147
+ return this.cachedResult;
148
+ }
149
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingManagementGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
150
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingManagementGuard, providedIn: 'root' }); }
151
+ }
152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingManagementGuard, decorators: [{
153
+ type: Injectable,
154
+ args: [{ providedIn: 'root' }]
155
+ }] });
156
+
157
+ const baseNode = new NavigatorNode({
158
+ label: gettext('Messaging service'),
159
+ icon: 'arrows-dotted-left-right',
160
+ path: 'monitoring/messaging-service',
161
+ priority: 100,
162
+ parent: {
163
+ label: gettext('Monitoring'),
164
+ icon: 'monitoring'
165
+ }
166
+ });
167
+ class MessagingNavigatorNodeFactory {
168
+ constructor() {
169
+ this.appState = inject(AppStateService);
170
+ this.navigatorService = inject(NavigatorService);
171
+ this.namespacesService = inject(MessagingNamespacesService);
172
+ this.guard = inject(MessagingManagementGuard);
173
+ this.currentTenantId = this.appState.currentTenant.pipe(filter(currentTenant => !!currentTenant), map(currentTenant => currentTenant.name), take(1));
174
+ this.navigatorNode$ = from(this.guard.canActivate()).pipe(filter(allowed => allowed), switchMap(() => this.currentTenantId), switchMap(currentTenantId => this.namespacesService.getNamespaces(currentTenantId)), map(({ namespaces }) => {
175
+ if (!namespaces?.length) {
176
+ return [];
177
+ }
178
+ namespaces.forEach(namespace => {
179
+ const label = NAMESPACE_PROPS[namespace.name]?.label || namespace.name;
180
+ const childNode = new NavigatorNode({
181
+ label,
182
+ path: `monitoring/messaging-service/namespace/${namespace.name}`,
183
+ icon: NAMESPACE_PROPS[namespace.name]?.icon,
184
+ routerLinkExact: false
185
+ });
186
+ baseNode.add(childNode);
187
+ });
188
+ return baseNode;
189
+ }), catchError(() => {
190
+ return [];
191
+ }), shareReplay(1));
192
+ }
193
+ get() {
194
+ return this.navigatorNode$;
195
+ }
196
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingNavigatorNodeFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
197
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingNavigatorNodeFactory, providedIn: 'root' }); }
198
+ }
199
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingNavigatorNodeFactory, decorators: [{
200
+ type: Injectable,
201
+ args: [{ providedIn: 'root' }]
202
+ }], ctorParameters: () => [] });
203
+
204
+ /**
205
+ * In case of limits in Messaging management, value of '-1' means that there is no limit.
206
+ */
207
+ class BacklogQuotaLimitPipe {
208
+ constructor() {
209
+ this.translateService = inject(TranslateService);
210
+ this.locale = inject(LOCALE_ID);
211
+ }
212
+ transform(value) {
213
+ if (value == null || isNaN(value)) {
214
+ return '-';
215
+ }
216
+ else if (value === -1) {
217
+ return this.translateService.instant(gettext('Unlimited` backlog quota`'));
218
+ }
219
+ else {
220
+ return formatNumber(value, this.locale);
221
+ }
222
+ }
223
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BacklogQuotaLimitPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
224
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: BacklogQuotaLimitPipe, isStandalone: true, name: "backlogQuotaLimit" }); }
225
+ }
226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: BacklogQuotaLimitPipe, decorators: [{
227
+ type: Pipe,
228
+ args: [{ name: 'backlogQuotaLimit', standalone: true }]
229
+ }] });
230
+
231
+ /**
232
+ * Usage component displays usage information in a form of e.g. "51% used".
233
+ * It can be used in two ways:
234
+ * 1. By providing `count` and `limit` inputs, it will calculate the usage percentage.
235
+ * 2. By providing `percentage` input, it will use the provided percentage value.
236
+ * Note: `percentage` input takes precedence over `count` and `limit` inputs.
237
+ */
238
+ class UsageComponent {
239
+ constructor() {
240
+ this.count = input(null);
241
+ this.limit = input(null);
242
+ /**
243
+ * Percentage of usage. Value range is from 0 to 100 (or more).
244
+ * For example, if 10% is used, this value should be provided as 10 (not 0.1).
245
+ */
246
+ this.percentage = input(null);
247
+ /**
248
+ * Usage as a fraction (e.g. if 50% is used, usage value will be 0.5)
249
+ */
250
+ this.usage = computed(() => this.percentage() != null ? this.percentage() / 100 : this.getUsage(this.count(), this.limit()));
251
+ this.status = computed(() => this.getStatus(this.usage()));
252
+ this.usageToDisplay = gettext('{{ percentageOfQuota }} used');
253
+ this.statusMap = {
254
+ danger: ['tag--danger'],
255
+ warning: ['tag--warning'],
256
+ success: ['tag--success']
257
+ };
258
+ }
259
+ /**
260
+ * Get usage as fraction of count and limit.
261
+ * E.g. if count is 5 and limit is 10, returned usage will be 0.5
262
+ * @param count Usage count
263
+ * @param limit Usage limit
264
+ * @returns Count divided by limit or null if count or limit is null or limit is -1 (indicates no limit)
265
+ */
266
+ getUsage(count, limit) {
267
+ if (count == null || limit == null || limit === -1) {
268
+ return null;
269
+ }
270
+ return count / limit;
271
+ }
272
+ getStatus(usage) {
273
+ if (usage == null) {
274
+ return null;
275
+ }
276
+ const percentage = usage * 100;
277
+ if (percentage >= 80) {
278
+ return 'danger';
279
+ }
280
+ else if (percentage >= 50) {
281
+ return 'warning';
282
+ }
283
+ else {
284
+ return 'success';
285
+ }
286
+ }
287
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UsageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
288
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: UsageComponent, isStandalone: true, selector: "app-usage", inputs: { count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: false, transformFunction: null }, percentage: { classPropertyName: "percentage", publicName: "percentage", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "d-contents" }, ngImport: i0, template: "<div\n class=\"tag no-pointer\"\n [ngClass]=\"statusMap[status()]\"\n *ngIf=\"usage() !== null\"\n>\n <i\n class=\"text-danger m-r-4 text-12\"\n c8yIcon=\"exclamation-circle\"\n *ngIf=\"status() === 'danger'\"\n ></i>\n <span>\n {{ usageToDisplay | translate: { percentageOfQuota: (usage() | percent: '1.0-2') } }}\n </span>\n</div>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: PercentPipe, name: "percent" }] }); }
289
+ }
290
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: UsageComponent, decorators: [{
291
+ type: Component,
292
+ args: [{ selector: 'app-usage', standalone: true, imports: [IconDirective, NgIf, NgClass, IconDirective, C8yTranslatePipe, PercentPipe], host: { class: 'd-contents' }, template: "<div\n class=\"tag no-pointer\"\n [ngClass]=\"statusMap[status()]\"\n *ngIf=\"usage() !== null\"\n>\n <i\n class=\"text-danger m-r-4 text-12\"\n c8yIcon=\"exclamation-circle\"\n *ngIf=\"status() === 'danger'\"\n ></i>\n <span>\n {{ usageToDisplay | translate: { percentageOfQuota: (usage() | percent: '1.0-2') } }}\n </span>\n</div>\n" }]
293
+ }] });
294
+
295
+ const DataType = {
296
+ publishers: 'publishers',
297
+ subscribers: 'subscribers',
298
+ topics: 'topics'
299
+ };
300
+ class NamespaceItemCardComponent {
301
+ constructor() {
302
+ this.DATA_TYPE = DataType;
303
+ this.ITEM_DETAILS = {
304
+ publishers: { icon: 'output', title: gettext('Publishers') },
305
+ subscribers: { icon: 'input', title: gettext('Subscribers') },
306
+ topics: { icon: 'day-view', title: gettext('Topics') }
307
+ };
308
+ this.topicsLimitLabel = gettext('Limit: {{ backlogQuotaLimit }}');
309
+ /**
310
+ * The label of the service (already translated).
311
+ */
312
+ this.serviceLabel = '';
313
+ }
314
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NamespaceItemCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
315
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: NamespaceItemCardComponent, isStandalone: true, selector: "app-namespace-item-card", inputs: { serviceLabel: "serviceLabel", limit: "limit", dataType: "dataType", value: "value" }, host: { classAttribute: "card m-b-0 fit-w" }, ngImport: i0, template: "<div class=\"card-block text-default visible-xs text-center p-b-0\">\n <span class=\"text-12 text-uppercase text-muted\">\n {{ serviceLabel | translate }}\n </span>\n</div>\n<div\n class=\"card-block text-default p-t-sm-48\"\n [ngClass]=\"{\n 'p-b-sm-48': limit === 0\n }\"\n>\n <div class=\"d-flex fit-w a-i-center gap-8 j-c-center\">\n <i\n class=\"icon-32 c8y-icon-duocolor\"\n [c8yIcon]=\"ITEM_DETAILS[dataType].icon\"\n ></i>\n <span class=\"h1\">\n {{ value | number }}\n </span>\n <span\n class=\"a-s-baseline text-14 text-medium text-truncate\"\n title=\"{{ ITEM_DETAILS[dataType].title | translate }}\"\n >\n {{ ITEM_DETAILS[dataType].title | translate }}\n </span>\n </div>\n</div>\n<div\n class=\"card-footer d-flex gap-16 j-c-center a-i-center\"\n *ngIf=\"dataType === DATA_TYPE.topics && limit !== 0\"\n>\n <span\n class=\"tag tag--default no-pointer\"\n data-cy=\"card-limit\"\n >\n {{ topicsLimitLabel | translate: { backlogQuotaLimit: limit | backlogQuotaLimit } }}\n </span>\n <app-usage\n [count]=\"value\"\n [limit]=\"limit\"\n ></app-usage>\n</div>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "ngmodule", type: C8yTranslateModule }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: BacklogQuotaLimitPipe, name: "backlogQuotaLimit" }, { kind: "component", type: UsageComponent, selector: "app-usage", inputs: ["count", "limit", "percentage"] }] }); }
316
+ }
317
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NamespaceItemCardComponent, decorators: [{
318
+ type: Component,
319
+ args: [{ selector: 'app-namespace-item-card', standalone: true, imports: [IconDirective, C8yTranslateModule, CommonModule, BacklogQuotaLimitPipe, UsageComponent], host: { class: 'card m-b-0 fit-w' }, template: "<div class=\"card-block text-default visible-xs text-center p-b-0\">\n <span class=\"text-12 text-uppercase text-muted\">\n {{ serviceLabel | translate }}\n </span>\n</div>\n<div\n class=\"card-block text-default p-t-sm-48\"\n [ngClass]=\"{\n 'p-b-sm-48': limit === 0\n }\"\n>\n <div class=\"d-flex fit-w a-i-center gap-8 j-c-center\">\n <i\n class=\"icon-32 c8y-icon-duocolor\"\n [c8yIcon]=\"ITEM_DETAILS[dataType].icon\"\n ></i>\n <span class=\"h1\">\n {{ value | number }}\n </span>\n <span\n class=\"a-s-baseline text-14 text-medium text-truncate\"\n title=\"{{ ITEM_DETAILS[dataType].title | translate }}\"\n >\n {{ ITEM_DETAILS[dataType].title | translate }}\n </span>\n </div>\n</div>\n<div\n class=\"card-footer d-flex gap-16 j-c-center a-i-center\"\n *ngIf=\"dataType === DATA_TYPE.topics && limit !== 0\"\n>\n <span\n class=\"tag tag--default no-pointer\"\n data-cy=\"card-limit\"\n >\n {{ topicsLimitLabel | translate: { backlogQuotaLimit: limit | backlogQuotaLimit } }}\n </span>\n <app-usage\n [count]=\"value\"\n [limit]=\"limit\"\n ></app-usage>\n</div>\n" }]
320
+ }], propDecorators: { serviceLabel: [{
321
+ type: Input
322
+ }], limit: [{
323
+ type: Input
324
+ }], dataType: [{
325
+ type: Input
326
+ }], value: [{
327
+ type: Input
328
+ }] } });
329
+
330
+ class NamespaceItemComponent {
331
+ constructor() {
332
+ this.translateService = inject(TranslateService);
333
+ this.namespaceName = '';
334
+ this.namespaceLabel = '';
335
+ this.icon = '';
336
+ this.namespace = {};
337
+ this.policies = {};
338
+ }
339
+ set _namespaceName(name) {
340
+ this.namespaceName = name;
341
+ this.icon = NAMESPACE_PROPS[name]?.icon;
342
+ this.namespaceLabel = this.translateService.instant(NAMESPACE_PROPS[name]?.label) || name;
343
+ }
344
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NamespaceItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
345
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: NamespaceItemComponent, isStandalone: true, selector: "app-namespace-item", inputs: { _namespaceName: ["namespaceName", "_namespaceName"], namespace: "namespace", policies: "policies" }, ngImport: i0, template: "<div class=\"d-flex-sm d-col-xs p-t-24 p-l-16 p-r-16 m-0 bg-level-1\">\n <div\n class=\"col-sm-3 m-b-24 col-xs-12 d-flex gap-16 text-default a-i-center j-c-center a-s-stretch\"\n >\n <div class=\"text-center d-col\">\n <i\n class=\"m-b-8 icon-40 c8y-icon-duocolor\"\n [c8yIcon]=\"icon\"\n ></i>\n <span class=\"tag tag--info\">{{ 'Service' | translate }}</span>\n </div>\n <span class=\"h4\">{{ namespaceLabel }}</span>\n </div>\n <div class=\"col-sm-3 m-b-24 col-xs-12 a-i-stretch d-flex\">\n <app-namespace-item-card\n [serviceLabel]=\"namespaceLabel\"\n [dataType]=\"'topics'\"\n [limit]=\"namespace?.topics?.limit\"\n [value]=\"namespace?.topics?.count\"\n ></app-namespace-item-card>\n </div>\n <div class=\"col-sm-3 m-b-24 col-xs-12 a-i-stretch d-flex\">\n <app-namespace-item-card\n [serviceLabel]=\"namespaceLabel\"\n [dataType]=\"'publishers'\"\n [value]=\"namespace?.publishers?.count\"\n ></app-namespace-item-card>\n </div>\n <div class=\"col-sm-3 m-b-24 col-xs-12 a-i-stretch d-flex\">\n <app-namespace-item-card\n [serviceLabel]=\"namespaceLabel\"\n [dataType]=\"'subscribers'\"\n [value]=\"namespace?.subscribers?.count\"\n ></app-namespace-item-card>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: NamespaceItemCardComponent, selector: "app-namespace-item-card", inputs: ["serviceLabel", "limit", "dataType", "value"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
346
+ }
347
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NamespaceItemComponent, decorators: [{
348
+ type: Component,
349
+ args: [{ selector: 'app-namespace-item', standalone: true, imports: [CommonModule, IconDirective, NamespaceItemCardComponent, C8yTranslatePipe], template: "<div class=\"d-flex-sm d-col-xs p-t-24 p-l-16 p-r-16 m-0 bg-level-1\">\n <div\n class=\"col-sm-3 m-b-24 col-xs-12 d-flex gap-16 text-default a-i-center j-c-center a-s-stretch\"\n >\n <div class=\"text-center d-col\">\n <i\n class=\"m-b-8 icon-40 c8y-icon-duocolor\"\n [c8yIcon]=\"icon\"\n ></i>\n <span class=\"tag tag--info\">{{ 'Service' | translate }}</span>\n </div>\n <span class=\"h4\">{{ namespaceLabel }}</span>\n </div>\n <div class=\"col-sm-3 m-b-24 col-xs-12 a-i-stretch d-flex\">\n <app-namespace-item-card\n [serviceLabel]=\"namespaceLabel\"\n [dataType]=\"'topics'\"\n [limit]=\"namespace?.topics?.limit\"\n [value]=\"namespace?.topics?.count\"\n ></app-namespace-item-card>\n </div>\n <div class=\"col-sm-3 m-b-24 col-xs-12 a-i-stretch d-flex\">\n <app-namespace-item-card\n [serviceLabel]=\"namespaceLabel\"\n [dataType]=\"'publishers'\"\n [value]=\"namespace?.publishers?.count\"\n ></app-namespace-item-card>\n </div>\n <div class=\"col-sm-3 m-b-24 col-xs-12 a-i-stretch d-flex\">\n <app-namespace-item-card\n [serviceLabel]=\"namespaceLabel\"\n [dataType]=\"'subscribers'\"\n [value]=\"namespace?.subscribers?.count\"\n ></app-namespace-item-card>\n </div>\n</div>\n" }]
350
+ }], propDecorators: { _namespaceName: [{
351
+ type: Input,
352
+ args: ['namespaceName']
353
+ }], namespace: [{
354
+ type: Input
355
+ }], policies: [{
356
+ type: Input
357
+ }] } });
358
+
359
+ class NamespaceListComponent {
360
+ constructor() {
361
+ this.alertService = inject(AlertService);
362
+ this.appState = inject(AppStateService);
363
+ this.namespacesService = inject(MessagingNamespacesService);
364
+ this.loading = true;
365
+ }
366
+ async ngOnInit() {
367
+ await this.reload();
368
+ }
369
+ async reload() {
370
+ this.loading = true;
371
+ try {
372
+ const currentTenantId = this.appState.currentTenant.value.name;
373
+ this.namespacesDetails = await this.namespacesService.getNamespacesDetails(currentTenantId);
374
+ }
375
+ catch (e) {
376
+ this.alertService.addServerFailure(e);
377
+ }
378
+ finally {
379
+ this.loading = false;
380
+ }
381
+ }
382
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NamespaceListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
383
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: NamespaceListComponent, isStandalone: true, selector: "app-namespace-list", ngImport: i0, template: "<c8y-title>{{ 'Messaging service' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'monitoring'\"\n [label]=\"'Monitoring' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"'Messaging service' | translate\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <li>\n <a\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"reload()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': loading }\"\n ></i>\n {{ 'Reload' | translate }}\n </a>\n </li>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/standard-tenant/monitoring/#messaging-service\"></c8y-help>\n\n<div\n class=\"interact-grid\"\n *ngIf=\"!loading; else loadingTemplate\"\n>\n <a\n class=\"card\"\n *ngFor=\"let namespace of namespacesDetails\"\n [routerLink]=\"['namespace', namespace.id]\"\n >\n <app-namespace-item\n [namespaceName]=\"namespace.id\"\n [namespace]=\"namespace.namespace\"\n ></app-namespace-item>\n </a>\n</div>\n\n<ng-template #loadingTemplate>\n <c8y-loading></c8y-loading>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: HeaderModule }, { kind: "component", type: i1$1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "ngmodule", type: HelpModule }, { kind: "component", type: i1$1.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "ngmodule", type: C8yTranslateModule }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }, { kind: "component", type: NamespaceItemComponent, selector: "app-namespace-item", inputs: ["namespaceName", "namespace", "policies"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i1$1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1$1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }] }); }
384
+ }
385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NamespaceListComponent, decorators: [{
386
+ type: Component,
387
+ args: [{ selector: 'app-namespace-list', imports: [
388
+ CommonModule,
389
+ HeaderModule,
390
+ HelpModule,
391
+ C8yTranslateModule,
392
+ NamespaceItemComponent,
393
+ RouterLink,
394
+ BreadcrumbModule,
395
+ ActionBarItemComponent,
396
+ IconDirective,
397
+ LoadingComponent
398
+ ], standalone: true, template: "<c8y-title>{{ 'Messaging service' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'monitoring'\"\n [label]=\"'Monitoring' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"'Messaging service' | translate\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <li>\n <a\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"reload()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': loading }\"\n ></i>\n {{ 'Reload' | translate }}\n </a>\n </li>\n</c8y-action-bar-item>\n\n<c8y-help src=\"/docs/standard-tenant/monitoring/#messaging-service\"></c8y-help>\n\n<div\n class=\"interact-grid\"\n *ngIf=\"!loading; else loadingTemplate\"\n>\n <a\n class=\"card\"\n *ngFor=\"let namespace of namespacesDetails\"\n [routerLink]=\"['namespace', namespace.id]\"\n >\n <app-namespace-item\n [namespaceName]=\"namespace.id\"\n [namespace]=\"namespace.namespace\"\n ></app-namespace-item>\n </a>\n</div>\n\n<ng-template #loadingTemplate>\n <c8y-loading></c8y-loading>\n</ng-template>\n" }]
399
+ }] });
400
+
401
+ class TimeToLivePipe {
402
+ constructor(translateService) {
403
+ this.translateService = translateService;
404
+ }
405
+ /**
406
+ * Transform time in seconds to human readable format.
407
+ * @param seconds time in seconds
408
+ * @returns human readable time period
409
+ */
410
+ transform(seconds) {
411
+ if (seconds == null || isNaN(seconds)) {
412
+ return '-';
413
+ }
414
+ if (seconds === -1) {
415
+ return this.translateService.instant(gettext('Unlimited` time-to-live period`'));
416
+ }
417
+ const minutes = Math.floor(seconds / 60);
418
+ const hours = Math.floor(minutes / 60);
419
+ const days = Math.floor(hours / 24);
420
+ if (days >= 1) {
421
+ const remainingHours = hours % 24;
422
+ if (days === 1) {
423
+ if (remainingHours === 0) {
424
+ return this.translateService.instant(gettext('1 day'));
425
+ }
426
+ if (remainingHours === 1) {
427
+ return this.translateService.instant(gettext('1 day 1 hour'));
428
+ }
429
+ return this.translateService.instant(gettext('1 day {{hours}} hours'), {
430
+ hours: remainingHours
431
+ });
432
+ }
433
+ else {
434
+ if (remainingHours === 0) {
435
+ return this.translateService.instant(gettext('{{days}} days'), { days });
436
+ }
437
+ if (remainingHours === 1) {
438
+ return this.translateService.instant(gettext('{{days}} days 1 hour'), { days });
439
+ }
440
+ return this.translateService.instant(gettext('{{days}} days {{hours}} hours'), {
441
+ days,
442
+ hours: remainingHours
443
+ });
444
+ }
445
+ }
446
+ if (hours > 0) {
447
+ const remainingMinutes = minutes % 60;
448
+ if (hours === 1) {
449
+ if (remainingMinutes === 0) {
450
+ return this.translateService.instant(gettext('1 hour'));
451
+ }
452
+ if (remainingMinutes === 1) {
453
+ return this.translateService.instant(gettext('1 hour 1 minute'));
454
+ }
455
+ return this.translateService.instant(gettext('1 hour {{minutes}} minutes'), {
456
+ minutes: remainingMinutes
457
+ });
458
+ }
459
+ else {
460
+ if (remainingMinutes === 0) {
461
+ return this.translateService.instant(gettext('{{hours}} hours'), { hours });
462
+ }
463
+ if (remainingMinutes === 1) {
464
+ return this.translateService.instant(gettext('{{hours}} hours 1 minute'), { hours });
465
+ }
466
+ return this.translateService.instant(gettext('{{hours}} hours {{minutes}} minutes'), {
467
+ hours,
468
+ minutes: remainingMinutes
469
+ });
470
+ }
471
+ }
472
+ if (minutes > 0) {
473
+ const remainingSeconds = seconds % 60;
474
+ if (minutes === 1) {
475
+ if (remainingSeconds === 0) {
476
+ return this.translateService.instant(gettext('1 minute'));
477
+ }
478
+ if (remainingSeconds === 1) {
479
+ return this.translateService.instant(gettext('1 minute 1 second'));
480
+ }
481
+ return this.translateService.instant(gettext('1 minute {{seconds}} seconds'), {
482
+ seconds: remainingSeconds
483
+ });
484
+ }
485
+ else {
486
+ if (remainingSeconds === 0) {
487
+ return this.translateService.instant(gettext('{{minutes}} minutes'), { minutes });
488
+ }
489
+ if (remainingSeconds === 1) {
490
+ return this.translateService.instant(gettext('{{minutes}} minutes 1 second'), {
491
+ minutes
492
+ });
493
+ }
494
+ return this.translateService.instant(gettext('{{minutes}} minutes {{seconds}} seconds'), {
495
+ minutes,
496
+ seconds: remainingSeconds
497
+ });
498
+ }
499
+ }
500
+ if (seconds === 1) {
501
+ return this.translateService.instant(gettext('1 second'));
502
+ }
503
+ else {
504
+ return this.translateService.instant(gettext('{{seconds}} seconds'), { seconds });
505
+ }
506
+ }
507
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimeToLivePipe, deps: [{ token: i1$2.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
508
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: TimeToLivePipe, isStandalone: true, name: "timeToLive" }); }
509
+ }
510
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TimeToLivePipe, decorators: [{
511
+ type: Pipe,
512
+ args: [{
513
+ name: 'timeToLive',
514
+ standalone: true
515
+ }]
516
+ }], ctorParameters: () => [{ type: i1$2.TranslateService }] });
517
+
518
+ class MessagingTopicsService extends Service {
519
+ constructor(client) {
520
+ super(client);
521
+ this.baseUrl = '/service/messaging-management';
522
+ this.listUrl = 'tenants';
523
+ }
524
+ async list(filter) {
525
+ const headers = { accept: 'application/json' };
526
+ const { tenant, namespace, ...params } = filter;
527
+ const url = `/${this.listUrl}/${tenant}/namespaces/${namespace}/topics`;
528
+ const res = await this.fetch(url, this.changeFetchOptions({ headers, params }, url));
529
+ const topicList = (await res.json());
530
+ const data = topicList.topics;
531
+ const paging = this.getPaging(topicList, filter);
532
+ return { res, data, paging };
533
+ }
534
+ async detail(filter) {
535
+ const headers = { accept: 'application/json' };
536
+ const { tenant, namespace, topic, type } = filter;
537
+ const url = `/${this.listUrl}/${tenant}/namespaces/${namespace}/topics/${topic}/types/${type}`;
538
+ const res = await this.fetch(url, this.changeFetchOptions({ headers }, url));
539
+ const data = await res.json();
540
+ return { res, data };
541
+ }
542
+ getPaging(topicList, filter) {
543
+ if (topicList.pageStatistics) {
544
+ const { currentPage, totalPages } = topicList.pageStatistics;
545
+ const statistics = {
546
+ ...topicList.pageStatistics,
547
+ nextPage: currentPage < totalPages ? currentPage + 1 : null,
548
+ prevPage: currentPage > 1 ? currentPage - 1 : null
549
+ };
550
+ return new Paging(this, statistics, filter);
551
+ }
552
+ return null;
553
+ }
554
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingTopicsService, deps: [{ token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
555
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingTopicsService, providedIn: 'root' }); }
556
+ }
557
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingTopicsService, decorators: [{
558
+ type: Injectable,
559
+ args: [{ providedIn: 'root' }]
560
+ }], ctorParameters: () => [{ type: i1.FetchClient }] });
561
+
562
+ class TopicsDataGridService {
563
+ constructor() {
564
+ this.topicsService = inject(MessagingTopicsService);
565
+ }
566
+ getColumns() {
567
+ return [
568
+ this.createColumn({
569
+ name: 'name',
570
+ header: gettext('Name'),
571
+ path: 'name',
572
+ filterable: true,
573
+ filteringConfig: {
574
+ fields: [
575
+ {
576
+ key: 'name',
577
+ type: 'input',
578
+ props: {
579
+ label: gettext('Filter topics by partial name'),
580
+ placeholder: 'myTopic',
581
+ required: true
582
+ }
583
+ }
584
+ ],
585
+ getFilter(model) {
586
+ return model.name;
587
+ }
588
+ }
589
+ }),
590
+ this.createColumn({
591
+ name: 'msgRateIn',
592
+ header: gettext('Message rate in (msg/s)'),
593
+ path: 'msgRateIn'
594
+ }),
595
+ this.createColumn({
596
+ name: 'msgRateOut',
597
+ header: gettext('Message rate out (msg/s)'),
598
+ path: 'msgRateOut'
599
+ }),
600
+ this.createColumn({
601
+ name: 'subscribers',
602
+ header: gettext('Subscribers'),
603
+ path: 'subscribers'
604
+ }),
605
+ this.createColumn({
606
+ name: 'backlogSize',
607
+ header: gettext('Message backlog'),
608
+ path: 'backlogSize'
609
+ }),
610
+ this.createColumn({
611
+ name: 'backlogUsagePercentage',
612
+ header: gettext('Used backlog'),
613
+ path: 'backlogUsagePercentage',
614
+ sortOrder: 'desc'
615
+ })
616
+ ];
617
+ }
618
+ async getServerSideData(tenantId, namespaceId, dataSourceModifier) {
619
+ const topicFilters = this.getTopicFilters(tenantId, namespaceId, dataSourceModifier);
620
+ const { res, data, paging } = await this.topicsService.list(topicFilters);
621
+ const filteredSize = paging.totalElements;
622
+ const size = (await this.topicsService.list({
623
+ ...topicFilters,
624
+ currentPage: 1,
625
+ pageSize: 1
626
+ })).paging.totalPages;
627
+ return {
628
+ res,
629
+ data,
630
+ paging,
631
+ size,
632
+ filteredSize
633
+ };
634
+ }
635
+ createColumn(columnProps) {
636
+ const column = new BaseColumn();
637
+ Object.assign(column, columnProps);
638
+ return column;
639
+ }
640
+ getTopicFilters(tenantId, namespaceId, dataSourceModifier) {
641
+ const topicFilters = {
642
+ tenant: tenantId,
643
+ namespace: namespaceId,
644
+ currentPage: dataSourceModifier.pagination.currentPage,
645
+ pageSize: dataSourceModifier.pagination.pageSize
646
+ };
647
+ return dataSourceModifier.columns.reduce((topicFilters, column) => {
648
+ if (column.filterable) {
649
+ if (column.filterPredicate) {
650
+ topicFilters[column.path] = column.filterPredicate;
651
+ }
652
+ if (column.externalFilterQuery) {
653
+ topicFilters[column.path] = column.filteringConfig.getFilter(column.externalFilterQuery);
654
+ }
655
+ }
656
+ if (column.sortable && column.sortOrder) {
657
+ const sortPath = column.sortingConfig?.pathSortingConfigs?.[0]?.path || column.path;
658
+ topicFilters.sort = `${sortPath},${column.sortOrder}`;
659
+ }
660
+ return topicFilters;
661
+ }, topicFilters);
662
+ }
663
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicsDataGridService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
664
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicsDataGridService, providedIn: 'root' }); }
665
+ }
666
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicsDataGridService, decorators: [{
667
+ type: Injectable,
668
+ args: [{ providedIn: 'root' }]
669
+ }] });
670
+
671
+ class TopicListViewComponent {
672
+ constructor() {
673
+ this.route = inject(ActivatedRoute);
674
+ this.appState = inject(AppStateService);
675
+ this.namespacesService = inject(MessagingNamespacesService);
676
+ this.topicsDataGridService = inject(TopicsDataGridService);
677
+ this.translateService = inject(TranslateService);
678
+ this.destroyRef = inject(DestroyRef);
679
+ this.loading$ = new BehaviorSubject(false);
680
+ this.refresh = new EventEmitter();
681
+ this.tenantId$ = this.appState.currentTenant.pipe(map(tenant => tenant.name));
682
+ this.namespaceId$ = this.route.params.pipe(map(params => params['namespace']));
683
+ this.namespaceLabel$ = this.namespaceId$.pipe(map(namespaceId => this.translateService.instant(NAMESPACE_PROPS[namespaceId].label)));
684
+ this.icon$ = this.namespaceId$.pipe(map(namespaceId => NAMESPACE_PROPS[namespaceId].icon));
685
+ this.namespaceDetails$ = combineLatest([this.tenantId$, this.namespaceId$, this.refresh]).pipe(tap(() => this.loading$.next(true)), switchMap(([tenantId, namespaceId]) => this.namespacesService.getNamespaceDetails(tenantId, namespaceId)), tap(() => this.loading$.next(false)), shareReplay(1));
686
+ this.tableTitle = gettext('Topics');
687
+ this.loadingItemsLabel = gettext('Loading topics...');
688
+ this.loadMoreItemsLabel = gettext('Load more topics');
689
+ this.noResultsMessage = gettext('No matching topics found.');
690
+ this.noResultsSubtitle = gettext('Refine your search terms or check your spelling.');
691
+ this.noDataMessage = gettext('No topics to display.');
692
+ this.noDataSubtitle = gettext('Create new topics to monitor them here.');
693
+ this.columns = this.topicsDataGridService.getColumns();
694
+ this.pagination = {
695
+ pageSize: 20,
696
+ currentPage: 1
697
+ };
698
+ this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
699
+ }
700
+ async onDataSourceModifier(dataSourceModifier) {
701
+ return firstValueFrom(combineLatest([this.tenantId$, this.namespaceId$]).pipe(switchMap(([tenantId, namespaceId]) => this.topicsDataGridService.getServerSideData(tenantId, namespaceId, dataSourceModifier))));
702
+ }
703
+ ngAfterViewInit() {
704
+ this.route.params
705
+ .pipe(takeUntilDestroyed(this.destroyRef))
706
+ .subscribe(() => this.refresh.emit());
707
+ }
708
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
709
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TopicListViewComponent, isStandalone: true, selector: "app-topic-list-view", ngImport: i0, template: "<c8y-title>{{ namespaceLabel$ | async }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'monitoring'\"\n [label]=\"'Monitoring' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [label]=\"'Messaging service' | translate\"\n [path]=\"'/monitoring/messaging-service'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"namespaceLabel$ | async\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <a\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"refresh.emit()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': loading$ | async }\"\n ></i>\n {{ 'Reload' | translate }}\n </a>\n</c8y-action-bar-item>\n\n<div class=\"card content-fullpage d-flex d-col\">\n <div class=\"bg-level-1 separator-bottom flex-no-shrink\">\n <div\n class=\"card-block\"\n style=\"min-height: 172px\"\n >\n <div\n class=\"col-md-4 m-b-24 col-xs-12 d-flex p-t-24 gap-16 text-default a-i-center a-s-stretch\"\n >\n <div class=\"text-center d-col\">\n <i\n class=\"m-b-8 icon-40 c8y-icon-duocolor\"\n [c8yIcon]=\"icon$ | async\"\n ></i>\n <span class=\"tag tag--info\">{{ 'Service' | translate }}</span>\n </div>\n <span class=\"h4 text-break-all\">{{ namespaceLabel$ | async }}</span>\n </div>\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend>\n {{ 'Service usage/limits' | translate }}\n </legend>\n\n <ng-container *ngIf=\"loading$ | async; else serviceUsageLimits\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #serviceUsageLimits>\n <ng-container *ngIf=\"(namespaceDetails$ | async)?.namespace as namespace\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Topics' | translate }}</label>\n <ng-container *ngIf=\"namespace?.topics?.limit !== 0\">\n <app-usage\n [count]=\"namespace?.topics?.count\"\n [limit]=\"namespace?.topics?.limit\"\n ></app-usage>\n <span class=\"m-l-16\">\n {{ namespace?.topics?.count | number }} /\n {{ namespace?.topics?.limit | backlogQuotaLimit }}\n </span>\n </ng-container>\n <ng-container *ngIf=\"namespace?.topics?.limit === 0\">\n <span class=\"m-l-16\">{{ namespace?.topics?.count | number }}</span>\n </ng-container>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Subscribers' | translate }}</label>\n <span class=\"m-l-16\">\n {{ namespace?.subscribers?.count | number }}\n </span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Publishers' | translate }}</label>\n <span class=\"m-l-16\">\n {{ namespace?.publishers?.count | number }}\n </span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend>{{ 'Service message backlog limits' | translate }}</legend>\n\n <ng-container *ngIf=\"loading$ | async; else serviceMessageBacklogLimits\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #serviceMessageBacklogLimits>\n <ng-container *ngIf=\"(namespaceDetails$ | async)?.policies as policies\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Backlog quota (per topic)' | translate }}\n </label>\n <span\n title=\"{{\n policies?.backlogQuota?.limit > 0\n ? (policies.backlogQuota.limit | bytes: 0 : true)\n : '-'\n }}\"\n >\n {{\n policies?.backlogQuota?.limit > 0\n ? (policies.backlogQuota.limit | bytes: 0)\n : '-'\n }}\n </span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Backlog time to live (TTL)' | translate }}\n </label>\n <span>{{ policies?.messageTTL | timeToLive }}</span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n </div>\n </div>\n\n <c8y-data-grid\n class=\"d-contents\"\n [title]=\"tableTitle | translate\"\n [loadingItemsLabel]=\"loadingItemsLabel | translate\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel | translate\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [refresh]=\"refresh\"\n [hideReload]=\"true\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'day-view'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n ></c8y-ui-empty-state>\n\n <c8y-column name=\"name\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <a\n title=\"{{ context.value }}\"\n [routerLink]=\"['topic', context.item.id]\"\n >\n {{ context.value }}\n </a>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"msgRateIn\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"msgRateOut\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"subscribers\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <a\n title=\"{{ context.value | number }}\"\n [routerLink]=\"['topic', context.item.id, 'subscribers']\"\n >\n {{ context.value | number }}\n </a>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"backlogSize\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | bytes: 2 : true }}\">\n {{ context.value | bytes: 2 }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"backlogUsagePercentage\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value / 100 | percent: '1.0-2' }}\">\n {{ context.value / 100 | percent: '1.0-2' }}\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: HeaderModule }, { kind: "component", type: i1$1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "ngmodule", type: DataGridModule }, { kind: "directive", type: i1$1.CellRendererDefDirective, selector: "[c8yCellRendererDef]" }, { kind: "directive", type: i1$1.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i1$1.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "hideReload"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: BacklogQuotaLimitPipe, name: "backlogQuotaLimit" }, { kind: "component", type: UsageComponent, selector: "app-usage", inputs: ["count", "limit", "percentage"] }, { kind: "pipe", type: BytesPipe, name: "bytes" }, { kind: "pipe", type: TimeToLivePipe, name: "timeToLive" }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i1$1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1$1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: PercentPipe, name: "percent" }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
710
+ }
711
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicListViewComponent, decorators: [{
712
+ type: Component,
713
+ args: [{ imports: [
714
+ RouterLink,
715
+ HeaderModule,
716
+ DataGridModule,
717
+ DecimalPipe,
718
+ C8yTranslatePipe,
719
+ BacklogQuotaLimitPipe,
720
+ UsageComponent,
721
+ BytesPipe,
722
+ TimeToLivePipe,
723
+ BreadcrumbModule,
724
+ LoadingComponent,
725
+ NgIf,
726
+ ActionBarItemComponent,
727
+ NgClass,
728
+ IconDirective,
729
+ PercentPipe,
730
+ EmptyStateComponent,
731
+ EmptyStateContextDirective,
732
+ AsyncPipe
733
+ ], selector: 'app-topic-list-view', standalone: true, template: "<c8y-title>{{ namespaceLabel$ | async }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'monitoring'\"\n [label]=\"'Monitoring' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [label]=\"'Messaging service' | translate\"\n [path]=\"'/monitoring/messaging-service'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"namespaceLabel$ | async\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <a\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"refresh.emit()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': loading$ | async }\"\n ></i>\n {{ 'Reload' | translate }}\n </a>\n</c8y-action-bar-item>\n\n<div class=\"card content-fullpage d-flex d-col\">\n <div class=\"bg-level-1 separator-bottom flex-no-shrink\">\n <div\n class=\"card-block\"\n style=\"min-height: 172px\"\n >\n <div\n class=\"col-md-4 m-b-24 col-xs-12 d-flex p-t-24 gap-16 text-default a-i-center a-s-stretch\"\n >\n <div class=\"text-center d-col\">\n <i\n class=\"m-b-8 icon-40 c8y-icon-duocolor\"\n [c8yIcon]=\"icon$ | async\"\n ></i>\n <span class=\"tag tag--info\">{{ 'Service' | translate }}</span>\n </div>\n <span class=\"h4 text-break-all\">{{ namespaceLabel$ | async }}</span>\n </div>\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend>\n {{ 'Service usage/limits' | translate }}\n </legend>\n\n <ng-container *ngIf=\"loading$ | async; else serviceUsageLimits\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #serviceUsageLimits>\n <ng-container *ngIf=\"(namespaceDetails$ | async)?.namespace as namespace\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Topics' | translate }}</label>\n <ng-container *ngIf=\"namespace?.topics?.limit !== 0\">\n <app-usage\n [count]=\"namespace?.topics?.count\"\n [limit]=\"namespace?.topics?.limit\"\n ></app-usage>\n <span class=\"m-l-16\">\n {{ namespace?.topics?.count | number }} /\n {{ namespace?.topics?.limit | backlogQuotaLimit }}\n </span>\n </ng-container>\n <ng-container *ngIf=\"namespace?.topics?.limit === 0\">\n <span class=\"m-l-16\">{{ namespace?.topics?.count | number }}</span>\n </ng-container>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Subscribers' | translate }}</label>\n <span class=\"m-l-16\">\n {{ namespace?.subscribers?.count | number }}\n </span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Publishers' | translate }}</label>\n <span class=\"m-l-16\">\n {{ namespace?.publishers?.count | number }}\n </span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend>{{ 'Service message backlog limits' | translate }}</legend>\n\n <ng-container *ngIf=\"loading$ | async; else serviceMessageBacklogLimits\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #serviceMessageBacklogLimits>\n <ng-container *ngIf=\"(namespaceDetails$ | async)?.policies as policies\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Backlog quota (per topic)' | translate }}\n </label>\n <span\n title=\"{{\n policies?.backlogQuota?.limit > 0\n ? (policies.backlogQuota.limit | bytes: 0 : true)\n : '-'\n }}\"\n >\n {{\n policies?.backlogQuota?.limit > 0\n ? (policies.backlogQuota.limit | bytes: 0)\n : '-'\n }}\n </span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Backlog time to live (TTL)' | translate }}\n </label>\n <span>{{ policies?.messageTTL | timeToLive }}</span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n </div>\n </div>\n\n <c8y-data-grid\n class=\"d-contents\"\n [title]=\"tableTitle | translate\"\n [loadingItemsLabel]=\"loadingItemsLabel | translate\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel | translate\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [refresh]=\"refresh\"\n [hideReload]=\"true\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'day-view'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n ></c8y-ui-empty-state>\n\n <c8y-column name=\"name\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <a\n title=\"{{ context.value }}\"\n [routerLink]=\"['topic', context.item.id]\"\n >\n {{ context.value }}\n </a>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"msgRateIn\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"msgRateOut\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"subscribers\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <a\n title=\"{{ context.value | number }}\"\n [routerLink]=\"['topic', context.item.id, 'subscribers']\"\n >\n {{ context.value | number }}\n </a>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"backlogSize\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | bytes: 2 : true }}\">\n {{ context.value | bytes: 2 }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"backlogUsagePercentage\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value / 100 | percent: '1.0-2' }}\">\n {{ context.value / 100 | percent: '1.0-2' }}\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n" }]
734
+ }] });
735
+
736
+ /**
737
+ * If the topic is persistent or not. If false, the topic is not saved and will be deleted if there are no clients.
738
+ */
739
+ var MessagingTopicType;
740
+ (function (MessagingTopicType) {
741
+ MessagingTopicType["Persistent"] = "persistent";
742
+ MessagingTopicType["NonPersistent"] = "non-persistent";
743
+ })(MessagingTopicType || (MessagingTopicType = {}));
744
+
745
+ class MessagingSubscribersService extends Service {
746
+ constructor(client) {
747
+ super(client);
748
+ this.baseUrl = '/service/messaging-management';
749
+ this.listUrl = 'tenants';
750
+ }
751
+ /**
752
+ * Get the list of subscribers for a topic.
753
+ *
754
+ * @param filter Subscriber filters.
755
+ */
756
+ async list(filter) {
757
+ const headers = { accept: 'application/json' };
758
+ const params = omit(filter, ['tenant', 'namespace', 'topic', 'type']);
759
+ const url = this.getBaseUrl(filter);
760
+ const res = await this.fetch(url, this.changeFetchOptions({ headers, params }, url));
761
+ const subscriberList = (await res.json());
762
+ const data = subscriberList.subscribers;
763
+ const paging = this.getPaging(subscriberList, filter);
764
+ return { res, data, paging };
765
+ }
766
+ async delete(subscriberToDelete) {
767
+ const method = 'DELETE';
768
+ const url = this.getBaseUrl(subscriberToDelete) + `/${subscriberToDelete.name}`;
769
+ const res = await this.fetch(url, this.changeFetchOptions({ method }, url));
770
+ return { res, data: null };
771
+ }
772
+ getPaging(json, filter) {
773
+ if (json.pageStatistics) {
774
+ const { currentPage, totalPages } = json.pageStatistics;
775
+ const statistics = {
776
+ ...json.pageStatistics,
777
+ nextPage: currentPage < totalPages ? currentPage + 1 : null,
778
+ prevPage: currentPage > 1 ? currentPage - 1 : null
779
+ };
780
+ return new Paging(this, statistics, filter);
781
+ }
782
+ return null;
783
+ }
784
+ getBaseUrl({ tenant, namespace, topic, type }) {
785
+ return `/${this.listUrl}/${tenant}/namespaces/${namespace}/topics/${topic}/types/${type}/subscribers`;
786
+ }
787
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingSubscribersService, deps: [{ token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
788
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingSubscribersService, providedIn: 'root' }); }
789
+ }
790
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingSubscribersService, decorators: [{
791
+ type: Injectable,
792
+ args: [{ providedIn: 'root' }]
793
+ }], ctorParameters: () => [{ type: i1.FetchClient }] });
794
+
795
+ class TopicSubscribersDataGridService {
796
+ constructor() {
797
+ this.subscribersService = inject(MessagingSubscribersService);
798
+ this.alertService = inject(AlertService);
799
+ this.modalService = inject(ModalService);
800
+ this.translateService = inject(TranslateService);
801
+ }
802
+ getColumns() {
803
+ return [
804
+ this.createColumn({
805
+ name: 'name',
806
+ header: gettext('Name'),
807
+ path: 'name',
808
+ filterable: true,
809
+ filteringConfig: {
810
+ fields: [
811
+ {
812
+ key: 'name',
813
+ type: 'input',
814
+ props: {
815
+ label: gettext('Filter subscribers by partial name'),
816
+ placeholder: 'mySubscriber',
817
+ required: true
818
+ }
819
+ }
820
+ ],
821
+ getFilter(model) {
822
+ return model.name;
823
+ }
824
+ }
825
+ }),
826
+ this.createColumn({
827
+ name: 'activeClients',
828
+ header: gettext('Connected clients'),
829
+ path: 'activeClients'
830
+ }),
831
+ this.createColumn({
832
+ name: 'messageAckRate',
833
+ header: gettext('Acknowledgment rate (msg/s)'),
834
+ path: 'messageAckRate'
835
+ }),
836
+ this.createColumn({
837
+ name: 'lastAcknowledgeDate',
838
+ header: gettext('Last acknowledged'),
839
+ path: 'lastAcknowledgeDate',
840
+ sortingConfig: {
841
+ pathSortingConfigs: [
842
+ {
843
+ path: 'lastAcknowledgeTimestamp'
844
+ }
845
+ ]
846
+ }
847
+ }),
848
+ this.createColumn({
849
+ name: 'unackMsgBacklog',
850
+ header: gettext('Unacknowledged messages'),
851
+ path: 'unackMsgBacklog'
852
+ }),
853
+ this.createColumn({
854
+ name: 'backlogUsagePercentage',
855
+ header: gettext('Used backlog'),
856
+ path: 'backlogUsagePercentage',
857
+ sortOrder: 'desc'
858
+ })
859
+ ];
860
+ }
861
+ async getServerSideData(tenantId, namespaceId, topicId, topicType, dataSourceModifier) {
862
+ const subscriberFilters = this.getSubscriberFilters(tenantId, namespaceId, topicId, topicType, dataSourceModifier);
863
+ const { res, data, paging } = await this.subscribersService.list(subscriberFilters);
864
+ const filteredSize = paging.totalElements;
865
+ const size = (await this.subscribersService.list({
866
+ ...subscriberFilters,
867
+ currentPage: 1,
868
+ pageSize: 1
869
+ })).paging.totalPages;
870
+ return {
871
+ res,
872
+ data: data.map(subscriber => ({
873
+ ...subscriber,
874
+ lastAcknowledgeDate: this.getLastAcknowledgeDate(subscriber),
875
+ id: subscriber.name
876
+ })),
877
+ paging,
878
+ size,
879
+ filteredSize
880
+ };
881
+ }
882
+ async unsubscribeSubscriber(subscriber, refreshCallback) {
883
+ const confirmationTitle = gettext('Unsubscribe "{{ subscriberName }}"?');
884
+ const confirmationMessage = subscriber.activeClients > 0
885
+ ? gettext('You are about to unsubscribe "{{ subscriberName }}" which has active clients. Do you want to proceed?')
886
+ : gettext('You are about to unsubscribe "{{ subscriberName }}". Do you want to proceed?');
887
+ try {
888
+ const subscriberName = subscriber.name;
889
+ await this.modalService.confirm(this.translateService.instant(confirmationTitle, {
890
+ subscriberName
891
+ }), this.translateService.instant(confirmationMessage, {
892
+ subscriberName
893
+ }), Status.DANGER, {
894
+ ok: gettext('Unsubscribe')
895
+ });
896
+ }
897
+ catch (e) {
898
+ // cancel
899
+ return;
900
+ }
901
+ try {
902
+ await this.subscribersService.delete(subscriber);
903
+ this.alertService.success(this.translateService.instant(gettext('Subscriber "{{ subscriberName }}" unsubscribed.'), {
904
+ subscriberName: subscriber.name
905
+ }));
906
+ }
907
+ catch (error) {
908
+ this.alertService.addServerFailure(error);
909
+ }
910
+ refreshCallback();
911
+ }
912
+ async bulkUnsubscribeSubscribers(subscribers, refreshCallback) {
913
+ const anySubscriberHasActiveClients = subscribers.some(subscriber => subscriber.activeClients > 0);
914
+ const confirmationTitle = gettext('Unsubscribe {{ numberOfSubscribers }} subscribers?');
915
+ const confirmationMessage = anySubscriberHasActiveClients
916
+ ? gettext('You are about to unsubscribe {{ numberOfSubscribers }} subscribers. Some of them have active clients. Do you want to proceed?')
917
+ : gettext('You are about to unsubscribe {{ numberOfSubscribers }} subscribers. Do you want to proceed?');
918
+ try {
919
+ const numberOfSubscribers = subscribers.length;
920
+ await this.modalService.confirm(this.translateService.instant(confirmationTitle, {
921
+ numberOfSubscribers
922
+ }), this.translateService.instant(confirmationMessage, {
923
+ numberOfSubscribers
924
+ }), Status.DANGER, {
925
+ ok: gettext('Unsubscribe')
926
+ });
927
+ }
928
+ catch (e) {
929
+ // cancel
930
+ return;
931
+ }
932
+ const results = await Promise.allSettled(subscribers.map(sub => this.subscribersService
933
+ .delete(sub)
934
+ .then(() => ({
935
+ success: true,
936
+ name: sub.name
937
+ }))
938
+ .catch(error => ({
939
+ success: false,
940
+ name: sub.name,
941
+ error
942
+ }))));
943
+ const failed = results
944
+ .filter(result => result.status === 'fulfilled' && result.value.success === false)
945
+ .map(result => result.value);
946
+ const succeeded = results
947
+ .filter(result => result.status === 'fulfilled' && result.value.success === true)
948
+ .map(result => result.value);
949
+ if (failed.length === 0) {
950
+ this.alertService.success(this.translateService.instant(gettext('{{ numberOfSubscribers }} subscribers unsubscribed.'), { numberOfSubscribers: succeeded.length }));
951
+ }
952
+ else if (succeeded.length === 0) {
953
+ const errorMessage = this.translateService.instant(gettext('Failed to unsubscribe {{ numberOfSubscribers }} subscribers.'), {
954
+ numberOfSubscribers: failed.length
955
+ });
956
+ const details = `${failed.map(f => `'${f.name}': ${f.error.data.message}`).join('\n')}`;
957
+ this.alertService.danger(errorMessage, details);
958
+ }
959
+ else {
960
+ // Some succeeded, some failed
961
+ const nonUnsubscribedSubscribers = failed.map(s => s.name).join(', ');
962
+ const message = this.translateService.instant(gettext(`Successfully unsubscribed {{ count }} subscribers.<br/>Failed to unsubscribe: {{ nonUnsubscribedSubscribers }}.`), { count: succeeded.length, nonUnsubscribedSubscribers });
963
+ const details = `${failed.map(f => `'${f.name}': ${f.error.data.message}`).join('\n')}`;
964
+ this.alertService.warning(message, details);
965
+ }
966
+ refreshCallback();
967
+ }
968
+ createColumn(columnProps) {
969
+ const column = new BaseColumn();
970
+ Object.assign(column, columnProps);
971
+ return column;
972
+ }
973
+ getSubscriberFilters(tenantId, namespaceId, topicId, topicType, dataSourceModifier) {
974
+ const subscriberFilters = {
975
+ tenant: tenantId,
976
+ namespace: namespaceId,
977
+ topic: topicId,
978
+ type: topicType,
979
+ currentPage: dataSourceModifier.pagination.currentPage,
980
+ pageSize: dataSourceModifier.pagination.pageSize
981
+ };
982
+ return dataSourceModifier.columns.reduce((subscriberFilters, column) => {
983
+ if (column.filterable) {
984
+ if (column.filterPredicate) {
985
+ subscriberFilters[column.path] = column.filterPredicate;
986
+ }
987
+ if (column.externalFilterQuery) {
988
+ subscriberFilters[column.path] = column.filteringConfig.getFilter(column.externalFilterQuery);
989
+ }
990
+ }
991
+ if (column.sortable && column.sortOrder) {
992
+ const sortPath = column.sortingConfig?.pathSortingConfigs?.[0]?.path || column.path;
993
+ subscriberFilters.sort = `${sortPath},${column.sortOrder}`;
994
+ }
995
+ return subscriberFilters;
996
+ }, subscriberFilters);
997
+ }
998
+ getLastAcknowledgeDate(subscriber) {
999
+ const lastAcknowledgeDate = new Date(subscriber.lastAcknowledgeTimestamp);
1000
+ return lastAcknowledgeDate.getTime() > 0 ? lastAcknowledgeDate : null;
1001
+ }
1002
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicSubscribersDataGridService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1003
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicSubscribersDataGridService, providedIn: 'root' }); }
1004
+ }
1005
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicSubscribersDataGridService, decorators: [{
1006
+ type: Injectable,
1007
+ args: [{ providedIn: 'root' }]
1008
+ }] });
1009
+
1010
+ class TopicSubscribersViewComponent {
1011
+ constructor() {
1012
+ this.route = inject(ActivatedRoute);
1013
+ this.appState = inject(AppStateService);
1014
+ this.namespacesService = inject(MessagingNamespacesService);
1015
+ this.topicsService = inject(MessagingTopicsService);
1016
+ this.topicSubscribersDataGridService = inject(TopicSubscribersDataGridService);
1017
+ this.translateService = inject(TranslateService);
1018
+ this.permissions = inject(Permissions);
1019
+ this.destroyRef = inject(DestroyRef);
1020
+ this.loading$ = new BehaviorSubject(false);
1021
+ this.refresh = new EventEmitter();
1022
+ this.tenantId$ = this.appState.currentTenant.pipe(map(tenant => tenant.name));
1023
+ this.namespaceId$ = this.route.params.pipe(map(params => params['namespace']));
1024
+ this.namespaceLabel$ = this.namespaceId$.pipe(map(namespaceId => this.translateService.instant(NAMESPACE_PROPS[namespaceId].label)));
1025
+ this.topicId$ = this.route.params.pipe(map(params => params['topic']));
1026
+ this.overview$ = combineLatest([this.tenantId$, this.namespaceId$, this.topicId$, this.refresh]).pipe(tap(() => this.loading$.next(true)), switchMap(async ([tenantId, namespaceId, topicId]) => ({
1027
+ topic: (await this.topicsService.detail({
1028
+ tenant: tenantId,
1029
+ namespace: namespaceId,
1030
+ topic: topicId,
1031
+ type: MessagingTopicType.Persistent
1032
+ })).data,
1033
+ policies: await this.namespacesService.getNamespacePolicies(tenantId, namespaceId)
1034
+ })), tap(() => this.loading$.next(false)), shareReplay(1));
1035
+ this.topicName$ = this.overview$.pipe(map(overview => overview.topic.name));
1036
+ this.tableTitle = gettext('Subscribers');
1037
+ this.loadingItemsLabel = gettext('Loading subscribers...');
1038
+ this.loadMoreItemsLabel = gettext('Load more subscribers');
1039
+ this.noResultsMessage = gettext('No matching subscribers found.');
1040
+ this.noResultsSubtitle = gettext('Refine your search terms or check your spelling.');
1041
+ this.noDataMessage = gettext('No subscribers to display.');
1042
+ this.noDataSubtitle = gettext('Create new subscribers to monitor them here.');
1043
+ this.columns = this.topicSubscribersDataGridService.getColumns();
1044
+ this.pagination = {
1045
+ pageSize: 20,
1046
+ currentPage: 1
1047
+ };
1048
+ this.selectable = true;
1049
+ this.actionControls = [
1050
+ {
1051
+ type: 'unsubscribeSubscriber',
1052
+ icon: 'unsubscribe',
1053
+ text: gettext('Unsubscribe'),
1054
+ callback: (subscriber) => this.unsubscribeSubscriber(subscriber),
1055
+ showIf: () => this.permissions.hasRole(Permissions.ROLE_TENANT_MANAGEMENT_ADMIN)
1056
+ }
1057
+ ];
1058
+ this.bulkActionControls = [
1059
+ {
1060
+ type: 'unsubscribeSubscriber',
1061
+ icon: 'unsubscribe',
1062
+ text: gettext('Unsubscribe'),
1063
+ callback: (subscribersNames) => this.bulkUnsubscribeSubscribers(subscribersNames),
1064
+ showIf: () => this.permissions.hasRole(Permissions.ROLE_TENANT_MANAGEMENT_ADMIN)
1065
+ }
1066
+ ];
1067
+ this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
1068
+ }
1069
+ async onDataSourceModifier(dataSourceModifier) {
1070
+ return firstValueFrom(combineLatest([this.tenantId$, this.namespaceId$, this.topicId$]).pipe(switchMap(([tenantId, namespaceId, topicId]) => this.topicSubscribersDataGridService.getServerSideData(tenantId, namespaceId, topicId, MessagingTopicType.Persistent, dataSourceModifier))));
1071
+ }
1072
+ ngAfterViewInit() {
1073
+ this.refresh.emit();
1074
+ }
1075
+ async unsubscribeSubscriber(subscriber) {
1076
+ const tenant = await firstValueFrom(this.tenantId$);
1077
+ const namespace = await firstValueFrom(this.namespaceId$);
1078
+ const topic = await firstValueFrom(this.topicId$);
1079
+ this.topicSubscribersDataGridService.unsubscribeSubscriber({
1080
+ ...subscriber,
1081
+ tenant,
1082
+ namespace,
1083
+ topic,
1084
+ type: MessagingTopicType.Persistent
1085
+ }, () => this.refresh.emit());
1086
+ }
1087
+ async bulkUnsubscribeSubscribers(subscribersNames) {
1088
+ const tenant = await firstValueFrom(this.tenantId$);
1089
+ const namespace = await firstValueFrom(this.namespaceId$);
1090
+ const topic = await firstValueFrom(this.topicId$);
1091
+ const allSubscribers = await firstValueFrom(this.dataGrid.dataSource.data$);
1092
+ const subscribers = subscribersNames.map(subscriberName => {
1093
+ const subscriber = allSubscribers.find(subscriber => subscriber.name === subscriberName);
1094
+ return {
1095
+ ...subscriber,
1096
+ tenant,
1097
+ namespace,
1098
+ topic,
1099
+ type: MessagingTopicType.Persistent
1100
+ };
1101
+ });
1102
+ this.topicSubscribersDataGridService.bulkUnsubscribeSubscribers(subscribers, () => this.refresh.emit());
1103
+ }
1104
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicSubscribersViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1105
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TopicSubscribersViewComponent, isStandalone: true, selector: "app-topic-subscribers-view", viewQueries: [{ propertyName: "dataGrid", first: true, predicate: DataGridComponent, descendants: true, static: true }], ngImport: i0, template: "<c8y-title>{{ topicName$ | async }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'monitoring'\"\n [label]=\"'Monitoring' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [label]=\"'Messaging service' | translate\"\n [path]=\"'/monitoring/messaging-service'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [label]=\"namespaceLabel$ | async\"\n [path]=\"'/monitoring/messaging-service/namespace/' + (namespaceId$ | async)\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"topicName$ | async\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <a\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"refresh.emit()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': loading$ | async }\"\n ></i>\n {{ 'Reload' | translate }}\n </a>\n</c8y-action-bar-item>\n\n<div class=\"card content-fullpage d-flex d-col\">\n <div class=\"bg-level-1 separator-bottom flex-no-shrink\">\n <div class=\"card-block\">\n <div\n class=\"col-md-4 m-b-24 col-xs-12 d-flex p-t-24 gap-16 text-default a-i-center a-s-stretch\"\n >\n <div class=\"text-center d-col\">\n <i\n class=\"m-b-8 icon-40 c8y-icon-duocolor\"\n [c8yIcon]=\"'day-view'\"\n ></i>\n <span class=\"tag tag--default\">{{ 'Topic' | translate }}</span>\n </div>\n <span class=\"h4\">{{ topicName$ | async }}</span>\n </div>\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend translate>Topic usage</legend>\n\n <ng-container *ngIf=\"loading$ | async; else topicSummary\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #topicSummary>\n <ng-container *ngIf=\"overview$ | async as overview\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Active subscribers' | translate }}</label>\n <span class=\"m-l-16\">{{ overview.topic.activeSubscribers | number }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Total subscribers' | translate }}</label>\n <span class=\"m-l-16\">{{ overview.topic.subscribers | number }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Total unacknowledged messages' | translate }}\n </label>\n <span class=\"m-l-16\">{{ overview.topic.unackMsgBacklog | number }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Message rate in' | translate }}\n </label>\n <span\n class=\"m-l-16\"\n ngNonBindable\n translate\n [translateParams]=\"{\n messagesPerSecond: overview.topic.msgRateIn | number\n }\"\n >\n {{ messagesPerSecond }} msg/s\n </span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Message rate out' | translate }}\n </label>\n <span\n class=\"m-l-16\"\n ngNonBindable\n translate\n [translateParams]=\"{\n messagesPerSecond: overview.topic.msgRateOut | number\n }\"\n >\n {{ messagesPerSecond }} msg/s\n </span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend translate>Topic message backlog</legend>\n\n <ng-container *ngIf=\"loading$ | async; else topicBacklog\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #topicBacklog>\n <ng-container *ngIf=\"overview$ | async as overview\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label\n class=\"small m-b-0 m-r-auto\"\n translate\n >\n Backlog usage\n </label>\n <app-usage [percentage]=\"overview.topic.backlogUsagePercentage\"></app-usage>\n <span class=\"m-l-16\">{{ overview.topic.backlogSize | bytes }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label\n class=\"small m-b-0 m-r-auto\"\n translate\n >\n Backlog quota\n </label>\n <span class=\"m-l-16\">\n {{\n overview.policies.backlogQuota?.limit > 0\n ? (overview.policies.backlogQuota?.limit | bytes)\n : '-'\n }}\n </span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n </div>\n </div>\n <c8y-data-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [title]=\"tableTitle | translate\"\n [loadingItemsLabel]=\"loadingItemsLabel | translate\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel | translate\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [refresh]=\"refresh\"\n [hideReload]=\"true\"\n [selectable]=\"selectable\"\n [actionControls]=\"actionControls\"\n [bulkActionControls]=\"bulkActionControls\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'input'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n ></c8y-ui-empty-state>\n\n <c8y-column name=\"name\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n {{ context.value }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"activeClients\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"messageAckRate\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"lastAcknowledgeDate\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ context.value }}\"\n *ngIf=\"context.value; else noLastAcknwoldegeDate\"\n >\n {{ context.value | relativeTime }}\n </span>\n <ng-template #noLastAcknwoldegeDate>&ndash;</ng-template>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"unackMsgBacklog\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"backlogUsagePercentage\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value / 100 | percent: '1.0-2' }}\">\n {{ context.value / 100 | percent: '1.0-2' }}\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n", dependencies: [{ kind: "ngmodule", type: DataGridModule }, { kind: "directive", type: i1$1.CellRendererDefDirective, selector: "[c8yCellRendererDef]" }, { kind: "directive", type: i1$1.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i1$1.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "hideReload"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "ngmodule", type: HeaderModule }, { kind: "component", type: i1$1.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "pipe", type: RelativeTimePipe, name: "relativeTime" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.PercentPipe, name: "percent" }, { kind: "pipe", type: BytesPipe, name: "bytes" }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i1$1.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i1$1.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: UsageComponent, selector: "app-usage", inputs: ["count", "limit", "percentage"] }, { kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }] }); }
1106
+ }
1107
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicSubscribersViewComponent, decorators: [{
1108
+ type: Component,
1109
+ args: [{ selector: 'app-topic-subscribers-view', standalone: true, imports: [
1110
+ DataGridModule,
1111
+ DecimalPipe,
1112
+ HeaderModule,
1113
+ C8yTranslateDirective,
1114
+ RelativeTimePipe,
1115
+ C8yTranslatePipe,
1116
+ CommonModule,
1117
+ BytesPipe,
1118
+ BreadcrumbModule,
1119
+ UsageComponent,
1120
+ ActionBarItemComponent,
1121
+ IconDirective,
1122
+ EmptyStateComponent,
1123
+ EmptyStateContextDirective,
1124
+ LoadingComponent
1125
+ ], template: "<c8y-title>{{ topicName$ | async }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n [icon]=\"'monitoring'\"\n [label]=\"'Monitoring' | translate\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [label]=\"'Messaging service' | translate\"\n [path]=\"'/monitoring/messaging-service'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [label]=\"namespaceLabel$ | async\"\n [path]=\"'/monitoring/messaging-service/namespace/' + (namespaceId$ | async)\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item [label]=\"topicName$ | async\"></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <a\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n (click)=\"refresh.emit()\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': loading$ | async }\"\n ></i>\n {{ 'Reload' | translate }}\n </a>\n</c8y-action-bar-item>\n\n<div class=\"card content-fullpage d-flex d-col\">\n <div class=\"bg-level-1 separator-bottom flex-no-shrink\">\n <div class=\"card-block\">\n <div\n class=\"col-md-4 m-b-24 col-xs-12 d-flex p-t-24 gap-16 text-default a-i-center a-s-stretch\"\n >\n <div class=\"text-center d-col\">\n <i\n class=\"m-b-8 icon-40 c8y-icon-duocolor\"\n [c8yIcon]=\"'day-view'\"\n ></i>\n <span class=\"tag tag--default\">{{ 'Topic' | translate }}</span>\n </div>\n <span class=\"h4\">{{ topicName$ | async }}</span>\n </div>\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend translate>Topic usage</legend>\n\n <ng-container *ngIf=\"loading$ | async; else topicSummary\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #topicSummary>\n <ng-container *ngIf=\"overview$ | async as overview\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Active subscribers' | translate }}</label>\n <span class=\"m-l-16\">{{ overview.topic.activeSubscribers | number }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">{{ 'Total subscribers' | translate }}</label>\n <span class=\"m-l-16\">{{ overview.topic.subscribers | number }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Total unacknowledged messages' | translate }}\n </label>\n <span class=\"m-l-16\">{{ overview.topic.unackMsgBacklog | number }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Message rate in' | translate }}\n </label>\n <span\n class=\"m-l-16\"\n ngNonBindable\n translate\n [translateParams]=\"{\n messagesPerSecond: overview.topic.msgRateIn | number\n }\"\n >\n {{ messagesPerSecond }} msg/s\n </span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label class=\"small m-b-0 m-r-auto\">\n {{ 'Message rate out' | translate }}\n </label>\n <span\n class=\"m-l-16\"\n ngNonBindable\n translate\n [translateParams]=\"{\n messagesPerSecond: overview.topic.msgRateOut | number\n }\"\n >\n {{ messagesPerSecond }} msg/s\n </span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n\n <div class=\"col-md-4\">\n <fieldset class=\"c8y-fieldset c8y-fieldset--lg\">\n <legend translate>Topic message backlog</legend>\n\n <ng-container *ngIf=\"loading$ | async; else topicBacklog\">\n <c8y-loading></c8y-loading>\n </ng-container>\n\n <ng-template #topicBacklog>\n <ng-container *ngIf=\"overview$ | async as overview\">\n <ul class=\"list-unstyled small animated fadeIn\">\n <li class=\"p-t-4 p-b-4 d-flex separator-bottom text-nowrap\">\n <label\n class=\"small m-b-0 m-r-auto\"\n translate\n >\n Backlog usage\n </label>\n <app-usage [percentage]=\"overview.topic.backlogUsagePercentage\"></app-usage>\n <span class=\"m-l-16\">{{ overview.topic.backlogSize | bytes }}</span>\n </li>\n\n <li class=\"p-t-4 p-b-4 d-flex text-nowrap\">\n <label\n class=\"small m-b-0 m-r-auto\"\n translate\n >\n Backlog quota\n </label>\n <span class=\"m-l-16\">\n {{\n overview.policies.backlogQuota?.limit > 0\n ? (overview.policies.backlogQuota?.limit | bytes)\n : '-'\n }}\n </span>\n </li>\n </ul>\n </ng-container>\n </ng-template>\n </fieldset>\n </div>\n </div>\n </div>\n <c8y-data-grid\n class=\"content-fullpage d-flex d-col border-top border-bottom\"\n [title]=\"tableTitle | translate\"\n [loadingItemsLabel]=\"loadingItemsLabel | translate\"\n [loadMoreItemsLabel]=\"loadMoreItemsLabel | translate\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [refresh]=\"refresh\"\n [hideReload]=\"true\"\n [selectable]=\"selectable\"\n [actionControls]=\"actionControls\"\n [bulkActionControls]=\"bulkActionControls\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'input'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n ></c8y-ui-empty-state>\n\n <c8y-column name=\"name\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value }}\">\n {{ context.value }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"activeClients\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"messageAckRate\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"lastAcknowledgeDate\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span\n title=\"{{ context.value }}\"\n *ngIf=\"context.value; else noLastAcknwoldegeDate\"\n >\n {{ context.value | relativeTime }}\n </span>\n <ng-template #noLastAcknwoldegeDate>&ndash;</ng-template>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"unackMsgBacklog\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value | number }}\">\n {{ context.value | number }}\n </span>\n </ng-container>\n </c8y-column>\n\n <c8y-column name=\"backlogUsagePercentage\">\n <ng-container *c8yCellRendererDef=\"let context\">\n <span title=\"{{ context.value / 100 | percent: '1.0-2' }}\">\n {{ context.value / 100 | percent: '1.0-2' }}\n </span>\n </ng-container>\n </c8y-column>\n </c8y-data-grid>\n</div>\n" }]
1126
+ }], propDecorators: { dataGrid: [{
1127
+ type: ViewChild,
1128
+ args: [DataGridComponent, { static: true }]
1129
+ }] } });
1130
+
1131
+ class TopicDetailsTabFactory {
1132
+ constructor(router) {
1133
+ this.router = router;
1134
+ }
1135
+ get() {
1136
+ const pattern = /\/namespace\/(.+)\/topic\/(.+)\/subscribers$/;
1137
+ const matches = this.router.url.match(pattern);
1138
+ if (matches) {
1139
+ const namespace = matches[1];
1140
+ const topic = matches[2];
1141
+ return [
1142
+ {
1143
+ path: basePath + `/namespace/${namespace}/topic/${topic}/subscribers`,
1144
+ priority: 500,
1145
+ label: gettext('Subscribers')
1146
+ }
1147
+ ];
1148
+ }
1149
+ return [];
1150
+ }
1151
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicDetailsTabFactory, deps: [{ token: i1$3.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
1152
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicDetailsTabFactory }); }
1153
+ }
1154
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TopicDetailsTabFactory, decorators: [{
1155
+ type: Injectable
1156
+ }], ctorParameters: () => [{ type: i1$3.Router }] });
1157
+
1158
+ class MessagingManagementModule {
1159
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1160
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: MessagingManagementModule }); }
1161
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingManagementModule, providers: [
1162
+ hookRoute([
1163
+ {
1164
+ path: basePath,
1165
+ component: NamespaceListComponent,
1166
+ canActivate: [MessagingManagementGuard]
1167
+ },
1168
+ {
1169
+ path: basePath + '/namespace/:namespace',
1170
+ component: TopicListViewComponent,
1171
+ canActivate: [MessagingManagementGuard]
1172
+ },
1173
+ {
1174
+ path: basePath + '/namespace/:namespace/topic/:topic',
1175
+ redirectTo: basePath + '/namespace/:namespace/topic/:topic/subscribers',
1176
+ pathMatch: 'full'
1177
+ },
1178
+ {
1179
+ path: basePath + '/namespace/:namespace/topic/:topic/subscribers',
1180
+ component: TopicSubscribersViewComponent,
1181
+ canActivate: [MessagingManagementGuard]
1182
+ }
1183
+ ]),
1184
+ hookTab(TopicDetailsTabFactory),
1185
+ hookNavigator(MessagingNavigatorNodeFactory)
1186
+ ] }); }
1187
+ }
1188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessagingManagementModule, decorators: [{
1189
+ type: NgModule,
1190
+ args: [{
1191
+ providers: [
1192
+ hookRoute([
1193
+ {
1194
+ path: basePath,
1195
+ component: NamespaceListComponent,
1196
+ canActivate: [MessagingManagementGuard]
1197
+ },
1198
+ {
1199
+ path: basePath + '/namespace/:namespace',
1200
+ component: TopicListViewComponent,
1201
+ canActivate: [MessagingManagementGuard]
1202
+ },
1203
+ {
1204
+ path: basePath + '/namespace/:namespace/topic/:topic',
1205
+ redirectTo: basePath + '/namespace/:namespace/topic/:topic/subscribers',
1206
+ pathMatch: 'full'
1207
+ },
1208
+ {
1209
+ path: basePath + '/namespace/:namespace/topic/:topic/subscribers',
1210
+ component: TopicSubscribersViewComponent,
1211
+ canActivate: [MessagingManagementGuard]
1212
+ }
1213
+ ]),
1214
+ hookTab(TopicDetailsTabFactory),
1215
+ hookNavigator(MessagingNavigatorNodeFactory)
1216
+ ]
1217
+ }]
1218
+ }] });
1219
+
1220
+ /**
1221
+ * Generated bundle index. Do not edit.
1222
+ */
1223
+
1224
+ export { MessagingManagementModule };
1225
+ //# sourceMappingURL=c8y-ngx-components-messaging-management.mjs.map