@reltio/components 1.4.2276 → 1.4.2278

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
- import { Entity, Metadata, Match, EntitiesMap } from '@reltio/mdm-sdk';
2
+ import { Entity, Metadata, EntitiesMap } from '@reltio/mdm-sdk';
3
3
  import { MatchRuleVariant } from '../types';
4
+ import type { MatchWithRuleLabels } from '../hooks/useMatchesLoader/types';
4
5
  type Props = {
5
6
  entitiesMap: EntitiesMap;
6
7
  entity: Entity;
7
- match: Match;
8
+ match: MatchWithRuleLabels;
8
9
  metadata: Metadata;
9
10
  variant?: MatchRuleVariant;
10
11
  };
@@ -1,3 +1,14 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
1
12
  import React from 'react';
2
13
  import { getMatchRules, getTransitiveMatchRules, isTransitiveMatch, NOT_MATCH } from '@reltio/mdm-sdk';
3
14
  import { SimpleMatchRulesBlock } from '../SimpleMatchRulesBlock';
@@ -11,7 +22,10 @@ export var MatchRulesBlock = function (_a) {
11
22
  return null;
12
23
  }
13
24
  var isTransitive = isTransitiveMatch(match);
14
- var matchRules = getMatchRules(metadata, entity.type, (match.matchRules || []).concat(match.negativeRules || []));
25
+ var matchRules = getMatchRules(metadata, entity.type, (match.matchRules || []).concat(match.negativeRules || [])).map(function (matchRule) {
26
+ var _a;
27
+ return (__assign(__assign({}, matchRule), { label: ((_a = match.matchRuleLabels) === null || _a === void 0 ? void 0 : _a[matchRule.uri]) || matchRule.label }));
28
+ });
15
29
  var rules = matchRules.filter(function (_a) {
16
30
  var uri = _a.uri;
17
31
  return uri !== NOT_MATCH;
@@ -163,6 +163,31 @@ describe('MatchRulesBlock test', function () {
163
163
  matchRules: [metadata.entityTypes[0].matchGroups[2]]
164
164
  }));
165
165
  });
166
+ it('should use data tenant match rule labels for rules that are not in customer tenant metadata', function () {
167
+ var _a;
168
+ var dataTenantMatchRuleUri = 'configuration/entityTypes/HCP/matchGroups/DataTenantRule';
169
+ var match = {
170
+ matchRules: [dataTenantMatchRuleUri],
171
+ matchRuleLabels: (_a = {},
172
+ _a[dataTenantMatchRuleUri] = 'Data Tenant Match Rule',
173
+ _a),
174
+ object: {
175
+ uri: 'entities/dataTenantEntity',
176
+ type: 'configuration/entityTypes/HCP',
177
+ dataTenant: 'dt1'
178
+ }
179
+ };
180
+ render(React.createElement(MatchRulesBlock, { match: match, metadata: metadata, entity: entity, entitiesMap: {} }));
181
+ expect(mockTransitiveMatchRules).not.toHaveBeenCalled();
182
+ expect(mockSimpleMatchRulesBuilder).toHaveBeenCalledWith(expect.objectContaining({
183
+ matchRules: [
184
+ {
185
+ uri: dataTenantMatchRuleUri,
186
+ label: 'Data Tenant Match Rule'
187
+ }
188
+ ]
189
+ }));
190
+ });
166
191
  it('should render SimpleMatchRulesBuilder for non transitive match and work correctly with negativeRules', function () {
167
192
  render(React.createElement(MatchRulesBlock, { match: potentialMatches[4], metadata: metadata, entity: entity, entitiesMap: {} }));
168
193
  expect(mockTransitiveMatchRules).not.toHaveBeenCalled();
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
- import { Entity, Metadata, Match, EntitiesMap } from '@reltio/mdm-sdk';
2
+ import { Entity, Metadata, EntitiesMap } from '@reltio/mdm-sdk';
3
3
  import { MatchRuleVariant } from '../types';
4
+ import type { MatchWithRuleLabels } from '../hooks/useMatchesLoader/types';
4
5
  type Props = {
5
6
  entitiesMap: EntitiesMap;
6
7
  entity: Entity;
7
- match: Match;
8
+ match: MatchWithRuleLabels;
8
9
  metadata: Metadata;
9
10
  variant?: MatchRuleVariant;
10
11
  };
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
15
  };
@@ -17,7 +28,10 @@ var MatchRulesBlock = function (_a) {
17
28
  return null;
18
29
  }
19
30
  var isTransitive = (0, mdm_sdk_1.isTransitiveMatch)(match);
20
- var matchRules = (0, mdm_sdk_1.getMatchRules)(metadata, entity.type, (match.matchRules || []).concat(match.negativeRules || []));
31
+ var matchRules = (0, mdm_sdk_1.getMatchRules)(metadata, entity.type, (match.matchRules || []).concat(match.negativeRules || [])).map(function (matchRule) {
32
+ var _a;
33
+ return (__assign(__assign({}, matchRule), { label: ((_a = match.matchRuleLabels) === null || _a === void 0 ? void 0 : _a[matchRule.uri]) || matchRule.label }));
34
+ });
21
35
  var rules = matchRules.filter(function (_a) {
22
36
  var uri = _a.uri;
23
37
  return uri !== mdm_sdk_1.NOT_MATCH;
@@ -168,6 +168,31 @@ describe('MatchRulesBlock test', function () {
168
168
  matchRules: [metadata.entityTypes[0].matchGroups[2]]
169
169
  }));
170
170
  });
171
+ it('should use data tenant match rule labels for rules that are not in customer tenant metadata', function () {
172
+ var _a;
173
+ var dataTenantMatchRuleUri = 'configuration/entityTypes/HCP/matchGroups/DataTenantRule';
174
+ var match = {
175
+ matchRules: [dataTenantMatchRuleUri],
176
+ matchRuleLabels: (_a = {},
177
+ _a[dataTenantMatchRuleUri] = 'Data Tenant Match Rule',
178
+ _a),
179
+ object: {
180
+ uri: 'entities/dataTenantEntity',
181
+ type: 'configuration/entityTypes/HCP',
182
+ dataTenant: 'dt1'
183
+ }
184
+ };
185
+ (0, react_2.render)(react_1.default.createElement(MatchRulesBlock_1.MatchRulesBlock, { match: match, metadata: metadata, entity: entity, entitiesMap: {} }));
186
+ expect(mockTransitiveMatchRules).not.toHaveBeenCalled();
187
+ expect(mockSimpleMatchRulesBuilder).toHaveBeenCalledWith(expect.objectContaining({
188
+ matchRules: [
189
+ {
190
+ uri: dataTenantMatchRuleUri,
191
+ label: 'Data Tenant Match Rule'
192
+ }
193
+ ]
194
+ }));
195
+ });
171
196
  it('should render SimpleMatchRulesBuilder for non transitive match and work correctly with negativeRules', function () {
172
197
  (0, react_2.render)(react_1.default.createElement(MatchRulesBlock_1.MatchRulesBlock, { match: potentialMatches[4], metadata: metadata, entity: entity, entitiesMap: {} }));
173
198
  expect(mockTransitiveMatchRules).not.toHaveBeenCalled();
@@ -36,6 +36,7 @@ var useAPI = function (config) {
36
36
  var _j = (0, react_1.useState)({}), customStyles = _j[0], setCustomStyles = _j[1];
37
37
  var workerRef = (0, react_1.useRef)(null);
38
38
  var entityRef = (0, react_1.useRef)(null);
39
+ var searchQueryRef = (0, react_1.useRef)(null);
39
40
  var requestListeners = (0, react_1.useRef)({});
40
41
  var responseListeners = (0, react_1.useRef)({});
41
42
  var sanitizeHtml = (0, useHtmlSanitizer_1.useHtmlSanitizer)();
@@ -54,7 +55,6 @@ var useAPI = function (config) {
54
55
  var reltioPath = (0, MdmModuleContext_1.useMdmReltioPath)();
55
56
  var workflowPath = (0, MdmModuleContext_1.useMdmWorkflowPath)();
56
57
  var user = (0, MdmModuleContext_1.useMdmUser)();
57
- var entityUri = (0, MdmModuleContext_1.useMdmEntityUri)();
58
58
  var isEditableMode = (0, MdmModuleContext_1.useMdmIsEditableMode)();
59
59
  var environment = (0, MdmModuleContext_1.useMdmWorkflowEnvironmentUrl)();
60
60
  var searchProviderData = (0, MdmModuleContext_1.useMdmSearchProviderData)();
@@ -62,6 +62,7 @@ var useAPI = function (config) {
62
62
  var innerText = '';
63
63
  var popupText = '';
64
64
  var process = function (_a) {
65
+ var _b;
65
66
  var task = _a.task, worker = _a.worker;
66
67
  if (task) {
67
68
  switch (task.action) {
@@ -147,11 +148,11 @@ var useAPI = function (config) {
147
148
  }
148
149
  case mdm_sdk_1.CustomAction.REQUEST: {
149
150
  var processedTask = (0, customScript_1.getProcessedTask)(task, apiPath, reltioPath, tenant);
150
- (0, API_1.processRequest)(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, servicesPath: servicesPath, apiPath: reltioPath, tenant: tenant, workflowPath: workflowPath, entity: entityRef.current, entityUri: entityUri, environment: environment, query: searchQuery, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
151
+ (0, API_1.processRequest)(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, servicesPath: servicesPath, apiPath: reltioPath, tenant: tenant, workflowPath: workflowPath, entity: entityRef.current, entityUri: (_b = entityRef.current) === null || _b === void 0 ? void 0 : _b.uri, environment: environment, query: searchQueryRef.current, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
151
152
  break;
152
153
  }
153
154
  case mdm_sdk_1.CustomAction.RETURN_PROCESSED_API_RESPONSE: {
154
- var _b = task.params, id = _b.id, data = _b.data;
155
+ var _c = task.params, id = _c.id, data = _c.data;
155
156
  if (responseListeners.current[id]) {
156
157
  responseListeners.current[id].next(data);
157
158
  responseListeners.current = (0, ramda_1.omit)([id], responseListeners.current);
@@ -159,7 +160,7 @@ var useAPI = function (config) {
159
160
  break;
160
161
  }
161
162
  case mdm_sdk_1.CustomAction.RETURN_PROCESSED_API_REQUEST: {
162
- var _c = task.params, id = _c.id, data = _c.data;
163
+ var _d = task.params, id = _d.id, data = _d.data;
163
164
  if (requestListeners.current[id]) {
164
165
  requestListeners.current[id].resolve(data);
165
166
  requestListeners.current = (0, ramda_1.omit)([id], requestListeners.current);
@@ -224,6 +225,7 @@ var useAPI = function (config) {
224
225
  postEventMessage('editMode', isEditableMode);
225
226
  }, [isEditableMode]);
226
227
  (0, react_1.useEffect)(function () {
228
+ searchQueryRef.current = searchQuery;
227
229
  postEventMessage('changeSearchQuery', searchQuery);
228
230
  }, [searchQuery]);
229
231
  (0, react_1.useEffect)(function () {
@@ -1040,6 +1040,37 @@ describe('useAPI custom sandbox behavior', function () {
1040
1040
  });
1041
1041
  expect(postMessage).toHaveBeenCalledWith(__assign({ action: 'response', result: 'entities/mnGg0XB' }, params));
1042
1042
  });
1043
+ it('should return the current entityUri on getEntityUri after navigating to another entity', function () {
1044
+ var updateMdmValues = setUp().updateMdmValues;
1045
+ (0, react_2.act)(function () {
1046
+ onmessageFromWorker({
1047
+ action: mdm_sdk_1.CustomAction.REQUEST,
1048
+ params: { name: mdm_sdk_1.RequestAction.GET_ENTITY_URI, id: 1 }
1049
+ });
1050
+ });
1051
+ expect(postMessage).toHaveBeenLastCalledWith({
1052
+ action: 'response',
1053
+ result: 'entities/mnGg0XB',
1054
+ name: mdm_sdk_1.RequestAction.GET_ENTITY_URI,
1055
+ id: 1
1056
+ });
1057
+ var newEntity = { uri: 'entities/NEW999', type: 'configuration/entityTypes/HCA', attributes: {} };
1058
+ (0, react_2.act)(function () {
1059
+ updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { entity: newEntity })); });
1060
+ });
1061
+ (0, react_2.act)(function () {
1062
+ onmessageFromWorker({
1063
+ action: mdm_sdk_1.CustomAction.REQUEST,
1064
+ params: { name: mdm_sdk_1.RequestAction.GET_ENTITY_URI, id: 2 }
1065
+ });
1066
+ });
1067
+ expect(postMessage).toHaveBeenLastCalledWith({
1068
+ action: 'response',
1069
+ result: 'entities/NEW999',
1070
+ name: mdm_sdk_1.RequestAction.GET_ENTITY_URI,
1071
+ id: 2
1072
+ });
1073
+ });
1043
1074
  it('should return entity on getEntity request', function () {
1044
1075
  setUp();
1045
1076
  var params = {
@@ -69,7 +69,6 @@ var useCustomScripts = function (config) {
69
69
  var apiPath = (0, MdmModuleContext_1.useMdmApiPath)();
70
70
  var workflowPath = (0, MdmModuleContext_1.useMdmWorkflowPath)();
71
71
  var user = (0, MdmModuleContext_1.useMdmUser)();
72
- var entityUri = (0, MdmModuleContext_1.useMdmEntityUri)();
73
72
  var environment = (0, MdmModuleContext_1.useMdmWorkflowEnvironmentUrl)();
74
73
  var workflowCheckPermission = (0, workflow_1.useWorkflowCheckPermission)();
75
74
  var searchProviderData = (0, MdmModuleContext_1.useMdmSearchProviderData)();
@@ -77,7 +76,10 @@ var useCustomScripts = function (config) {
77
76
  var requestListeners = (0, react_1.useRef)({});
78
77
  var responseListeners = (0, react_1.useRef)({});
79
78
  var workers = (0, react_1.useRef)({});
79
+ var entityRef = (0, react_1.useRef)(null);
80
+ var searchQueryRef = (0, react_1.useRef)(null);
80
81
  var processMessageFromWorker = function (_a) {
82
+ var _b;
81
83
  var task = _a.task, worker = _a.worker, config = _a.config;
82
84
  if (task) {
83
85
  switch (task.action) {
@@ -91,11 +93,11 @@ var useCustomScripts = function (config) {
91
93
  }
92
94
  case mdm_sdk_1.CustomAction.REQUEST: {
93
95
  var processedTask = (0, customScript_1.getProcessedTask)(task, apiPath, reltioPath, tenant);
94
- (0, useAPI_1.processRequest)(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, apiPath: reltioPath, servicesPath: servicesPath, tenant: tenant, workflowPath: workflowPath, entity: entity, entityUri: entityUri, environment: environment, query: searchQuery, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
96
+ (0, useAPI_1.processRequest)(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, apiPath: reltioPath, servicesPath: servicesPath, tenant: tenant, workflowPath: workflowPath, entity: entityRef.current, entityUri: (_b = entityRef.current) === null || _b === void 0 ? void 0 : _b.uri, environment: environment, query: searchQueryRef.current, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
95
97
  break;
96
98
  }
97
99
  case mdm_sdk_1.CustomAction.RETURN_PROCESSED_API_RESPONSE: {
98
- var _b = task.params, id = _b.id, data = _b.data;
100
+ var _c = task.params, id = _c.id, data = _c.data;
99
101
  if (responseListeners.current[id]) {
100
102
  responseListeners.current[id].next(data);
101
103
  responseListeners.current = (0, ramda_1.omit)([id], responseListeners.current);
@@ -103,7 +105,7 @@ var useCustomScripts = function (config) {
103
105
  break;
104
106
  }
105
107
  case mdm_sdk_1.CustomAction.RETURN_PROCESSED_API_REQUEST: {
106
- var _c = task.params, id = _c.id, data = _c.data;
108
+ var _d = task.params, id = _d.id, data = _d.data;
107
109
  if (requestListeners.current[id]) {
108
110
  requestListeners.current[id].resolve(data);
109
111
  requestListeners.current = (0, ramda_1.omit)([id], requestListeners.current);
@@ -127,6 +129,12 @@ var useCustomScripts = function (config) {
127
129
  };
128
130
  }
129
131
  }, [config, metadata]);
132
+ (0, react_1.useEffect)(function () {
133
+ entityRef.current = entity;
134
+ }, [entity]);
135
+ (0, react_1.useEffect)(function () {
136
+ searchQueryRef.current = searchQuery;
137
+ }, [searchQuery]);
130
138
  var customScriptRequestInterceptor = function (_a) {
131
139
  var _b;
132
140
  var url = _a.url, next = _a.next, resolve = _a.resolve, _c = _a.options, options = _c === void 0 ? {} : _c;
@@ -122,13 +122,16 @@ describe('useCustomScripts', function () {
122
122
  };
123
123
  var setUp = function (_a) {
124
124
  var _b = _a === void 0 ? {} : _a, _c = _b.mdmValues, mdmValues = _c === void 0 ? defaultMdmValues : _c, _d = _b.config, config = _d === void 0 ? createConfig() : _d;
125
+ var updateMdmValues;
125
126
  var Providers = function (_a) {
126
127
  var children = _a.children;
127
- return (react_1.default.createElement(MdmModuleContext_1.MdmModuleProvider, { values: mdmValues },
128
+ var _b = react_1.default.useState(mdmValues), mdmValuesState = _b[0], setMdmValuesState = _b[1];
129
+ updateMdmValues = setMdmValuesState;
130
+ return (react_1.default.createElement(MdmModuleContext_1.MdmModuleProvider, { values: mdmValuesState },
128
131
  react_1.default.createElement(SnackbarContext_1.SnackbarContext.Provider, { value: showSnackbarMessage },
129
132
  react_1.default.createElement(InterceptHandlersContext_1.InterceptHandlersContext.Provider, { value: interceptors }, children))));
130
133
  };
131
- return (0, react_2.renderHook)(useCustomScripts_1.useCustomScripts, { initialProps: config, wrapper: Providers });
134
+ return __assign(__assign({}, (0, react_2.renderHook)(useCustomScripts_1.useCustomScripts, { initialProps: config, wrapper: Providers })), { updateMdmValues: updateMdmValues });
132
135
  };
133
136
  var postMessage = jest.fn();
134
137
  var onmessageFromWorker;
@@ -488,4 +491,77 @@ describe('useCustomScripts', function () {
488
491
  }
489
492
  });
490
493
  }); });
494
+ it('should resolve getEntityUri/getEntity with the current entity after navigation', function () {
495
+ var entityA = { uri: 'entities/AAA', type: 'configuration/entityTypes/HCA', attributes: {} };
496
+ var entityB = { uri: 'entities/BBB', type: 'configuration/entityTypes/HCA', attributes: {} };
497
+ var updateMdmValues = setUp({ mdmValues: __assign(__assign({}, defaultMdmValues), { entity: entityA }) }).updateMdmValues;
498
+ (0, react_2.act)(function () {
499
+ onmessageFromWorker({
500
+ action: mdm_sdk_1.CustomAction.REQUEST,
501
+ params: { name: mdm_sdk_1.RequestAction.GET_ENTITY_URI, id: 'id1' }
502
+ });
503
+ });
504
+ expect(postMessage).toHaveBeenLastCalledWith({
505
+ action: 'response',
506
+ result: 'entities/AAA',
507
+ name: mdm_sdk_1.RequestAction.GET_ENTITY_URI,
508
+ id: 'id1'
509
+ });
510
+ (0, react_2.act)(function () {
511
+ updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { entity: entityB })); });
512
+ });
513
+ (0, react_2.act)(function () {
514
+ onmessageFromWorker({
515
+ action: mdm_sdk_1.CustomAction.REQUEST,
516
+ params: { name: mdm_sdk_1.RequestAction.GET_ENTITY_URI, id: 'id2' }
517
+ });
518
+ });
519
+ expect(postMessage).toHaveBeenLastCalledWith({
520
+ action: 'response',
521
+ result: 'entities/BBB',
522
+ name: mdm_sdk_1.RequestAction.GET_ENTITY_URI,
523
+ id: 'id2'
524
+ });
525
+ (0, react_2.act)(function () {
526
+ onmessageFromWorker({ action: mdm_sdk_1.CustomAction.REQUEST, params: { name: mdm_sdk_1.RequestAction.GET_ENTITY, id: 'id3' } });
527
+ });
528
+ expect(postMessage).toHaveBeenLastCalledWith({
529
+ action: 'response',
530
+ result: entityB,
531
+ name: mdm_sdk_1.RequestAction.GET_ENTITY,
532
+ id: 'id3'
533
+ });
534
+ });
535
+ it('should resolve getSearchQuery with the current query after it changes', function () {
536
+ var updateMdmValues = setUp({
537
+ mdmValues: __assign(__assign({}, defaultMdmValues), { searchProviderData: { type: 'search', data: { query: 'q1' } } })
538
+ }).updateMdmValues;
539
+ (0, react_2.act)(function () {
540
+ onmessageFromWorker({
541
+ action: mdm_sdk_1.CustomAction.REQUEST,
542
+ params: { name: mdm_sdk_1.RequestAction.GET_SEARCH_QUERY, id: 'id1' }
543
+ });
544
+ });
545
+ expect(postMessage).toHaveBeenLastCalledWith({
546
+ action: 'response',
547
+ result: 'q1',
548
+ name: mdm_sdk_1.RequestAction.GET_SEARCH_QUERY,
549
+ id: 'id1'
550
+ });
551
+ (0, react_2.act)(function () {
552
+ updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { searchProviderData: { type: 'search', data: { query: 'q2' } } })); });
553
+ });
554
+ (0, react_2.act)(function () {
555
+ onmessageFromWorker({
556
+ action: mdm_sdk_1.CustomAction.REQUEST,
557
+ params: { name: mdm_sdk_1.RequestAction.GET_SEARCH_QUERY, id: 'id2' }
558
+ });
559
+ });
560
+ expect(postMessage).toHaveBeenLastCalledWith({
561
+ action: 'response',
562
+ result: 'q2',
563
+ name: mdm_sdk_1.RequestAction.GET_SEARCH_QUERY,
564
+ id: 'id2'
565
+ });
566
+ });
491
567
  });
@@ -0,0 +1,4 @@
1
+ import { Match } from '@reltio/mdm-sdk';
2
+ export type MatchWithRuleLabels = Match & {
3
+ matchRuleLabels?: Record<string, string>;
4
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,4 +1,5 @@
1
- import { Entity, Match, SortingField, EntitiesMap } from '@reltio/mdm-sdk';
1
+ import { Entity, SortingField, EntitiesMap } from '@reltio/mdm-sdk';
2
+ import type { MatchWithRuleLabels } from './types';
2
3
  type Props = {
3
4
  enabled: boolean;
4
5
  entity: Entity;
@@ -17,7 +18,7 @@ type Props = {
17
18
  };
18
19
  export declare const useMatchesLoader: ({ enabled, entity, filter, rules, onFinishLoading, onStartLoading, options, page, rowsPerPage, sorting, markMatchedValues }: Props) => {
19
20
  total: number;
20
- matches: Match[];
21
+ matches: MatchWithRuleLabels[];
21
22
  entitiesMap: EntitiesMap<Pick<Entity, "label" | "type" | "uri">>;
22
23
  loadMatches: () => void;
23
24
  };
@@ -17,18 +17,48 @@ var mdm_sdk_1 = require("@reltio/mdm-sdk");
17
17
  var usePagingSimulator_1 = require("../usePagingSimulator");
18
18
  var useSafePromise_1 = require("../useSafePromise");
19
19
  var MdmModuleContext_1 = require("../../contexts/MdmModuleContext");
20
+ var matchRuleLabelsCache = new Map();
21
+ // Match rule labels are static per (dtssPath, customerTenant), so the request is
22
+ // cached and reused across reloads (paging, filtering, sorting) instead of being
23
+ // refetched every time matches are loaded. Failed requests are evicted so a later
24
+ // load can retry.
25
+ var getCachedDataTenantMatchRuleLabels = function (dtssPath, customerTenant) {
26
+ var cacheKey = "".concat(dtssPath, "::").concat(customerTenant);
27
+ var cachedRequest = matchRuleLabelsCache.get(cacheKey);
28
+ if (cachedRequest !== undefined) {
29
+ return cachedRequest;
30
+ }
31
+ var request = (0, mdm_sdk_1.getDataTenantMatchGroups)({ dtssPath: dtssPath, customerTenant: customerTenant }).catch(function () {
32
+ matchRuleLabelsCache.delete(cacheKey);
33
+ return {};
34
+ });
35
+ matchRuleLabelsCache.set(cacheKey, request);
36
+ return request;
37
+ };
38
+ var addDataTenantMatchRuleLabels = function (matches, entity, matchRuleLabels) {
39
+ if (!Object.keys(matchRuleLabels).length) {
40
+ return matches;
41
+ }
42
+ return matches.map(function (match) {
43
+ var _a;
44
+ return ((0, mdm_sdk_1.isDataTenantEntity)(entity) || (0, mdm_sdk_1.isDataTenantEntity)(match.object)) &&
45
+ ((_a = match.matchRules) === null || _a === void 0 ? void 0 : _a.some(function (matchRuleUri) { return matchRuleLabels[matchRuleUri]; }))
46
+ ? __assign(__assign({}, match), { matchRuleLabels: matchRuleLabels }) : match;
47
+ });
48
+ };
20
49
  var useMatchesLoader = function (_a) {
21
50
  var enabled = _a.enabled, entity = _a.entity, filter = _a.filter, rules = _a.rules, onFinishLoading = _a.onFinishLoading, onStartLoading = _a.onStartLoading, options = _a.options, page = _a.page, rowsPerPage = _a.rowsPerPage, sorting = _a.sorting, markMatchedValues = _a.markMatchedValues;
22
51
  var _b = sorting || {}, order = _b.order, field = _b.field;
23
52
  var _c = (0, react_1.useState)(0), total = _c[0], setTotal = _c[1];
24
53
  var _d = (0, react_1.useState)([]), matches = _d[0], setMatches = _d[1];
25
- var _e = (0, react_1.useState)({}), entitiesMap = _e[0], setEntitiesMap = _e[1];
54
+ var _e = (0, react_1.useState)({}), matchRuleLabels = _e[0], setMatchRuleLabels = _e[1];
55
+ var _f = (0, react_1.useState)({}), entitiesMap = _f[0], setEntitiesMap = _f[1];
26
56
  var metadata = (0, MdmModuleContext_1.useMdmMetadata)();
27
57
  var tenant = (0, MdmModuleContext_1.useMdmTenant)();
28
58
  var dtssPath = (0, MdmModuleContext_1.useMdmDtssPath)();
29
59
  var dataTenants = (0, MdmModuleContext_1.useMdmDataTenantsWithIdBuilder)();
30
60
  var showMasking = (0, MdmModuleContext_1.useMdmShowMasking)();
31
- var _f = (0, MdmModuleContext_1.useMdmGlobalSearchRequestOptions)() || {}, activityFilter = _f.activityFilter, globalFilter = _f.globalFilter;
61
+ var _g = (0, MdmModuleContext_1.useMdmGlobalSearchRequestOptions)() || {}, activityFilter = _g.activityFilter, globalFilter = _g.globalFilter;
32
62
  var activeness = typeof options.showInactiveEntities === 'boolean'
33
63
  ? options.showInactiveEntities
34
64
  ? mdm_sdk_1.ActivityFilter.ALL
@@ -51,6 +81,7 @@ var useMatchesLoader = function (_a) {
51
81
  var getTransitiveMatchesWithPaging = (0, usePagingSimulator_1.usePagingSimulator)(getTransitiveMatchesWithDtss);
52
82
  var getPagedMatchesForDataTenantEntity = (0, usePagingSimulator_1.usePagingSimulator)(mdm_sdk_1.getMatchesForDataTenantEntity);
53
83
  var safePromise = (0, useSafePromise_1.useSafePromise)();
84
+ var safeMatchRuleLabelsPromise = (0, useSafePromise_1.useSafePromise)();
54
85
  var getMatches = (0, react_1.useCallback)(function (force) {
55
86
  if (force === void 0) { force = false; }
56
87
  if (!enabled || (0, mdm_sdk_1.isTempUri)(entityUri)) {
@@ -60,7 +91,7 @@ var useMatchesLoader = function (_a) {
60
91
  return;
61
92
  }
62
93
  onStartLoading === null || onStartLoading === void 0 ? void 0 : onStartLoading();
63
- safePromise((0, mdm_sdk_1.isDataTenantEntity)(entity)
94
+ var matchesRequest = (0, mdm_sdk_1.isDataTenantEntity)(entity)
64
95
  ? getPagedMatchesForDataTenantEntity({
65
96
  dtssPath: dtssPath,
66
97
  entity: entity,
@@ -80,7 +111,8 @@ var useMatchesLoader = function (_a) {
80
111
  markMatchedValues: markMatchedValues,
81
112
  deep: options.showTransitiveMatches ? undefined : 1,
82
113
  activeness: activeness
83
- }, showMasking)), { offset: page * rowsPerPage, force: force })))
114
+ }, showMasking)), { offset: page * rowsPerPage, force: force }));
115
+ safePromise(matchesRequest)
84
116
  .then(function (result) {
85
117
  var response = result.response, total = result.total, originalResponse = result.originalResponse;
86
118
  setTotal(total);
@@ -126,12 +158,27 @@ var useMatchesLoader = function (_a) {
126
158
  (0, react_1.useEffect)(function () {
127
159
  getMatches();
128
160
  }, [getMatches]);
161
+ // Match rule labels are loaded independently of the matches request so the
162
+ // matches list renders without waiting for them; they are applied as a
163
+ // derived layer once available. Keyed on a stable boolean rather than the
164
+ // dataTenants array reference to avoid re-firing on every render.
165
+ var hasDataTenants = !!(dataTenants === null || dataTenants === void 0 ? void 0 : dataTenants.length);
166
+ (0, react_1.useEffect)(function () {
167
+ if (!dtssPath || !tenant || !hasDataTenants) {
168
+ setMatchRuleLabels({});
169
+ return;
170
+ }
171
+ safeMatchRuleLabelsPromise(getCachedDataTenantMatchRuleLabels(dtssPath, tenant))
172
+ .then(setMatchRuleLabels)
173
+ .catch(function () { return setMatchRuleLabels({}); });
174
+ }, [dtssPath, tenant, hasDataTenants, safeMatchRuleLabelsPromise]);
175
+ var matchesWithRuleLabels = (0, react_1.useMemo)(function () { return addDataTenantMatchRuleLabels(matches, entity, matchRuleLabels); }, [matches, entity, matchRuleLabels]);
129
176
  var loadMatches = (0, react_1.useCallback)(function () {
130
177
  getMatches(true);
131
178
  }, [getMatches]);
132
179
  return {
133
180
  total: total,
134
- matches: matches,
181
+ matches: matchesWithRuleLabels,
135
182
  entitiesMap: entitiesMap,
136
183
  loadMatches: loadMatches
137
184
  };
@@ -55,7 +55,7 @@ var mdm_sdk_1 = require("@reltio/mdm-sdk");
55
55
  var react_2 = require("@testing-library/react");
56
56
  var MdmModuleContext_1 = require("../../contexts/MdmModuleContext");
57
57
  var useMatchesLoader_1 = require("./useMatchesLoader");
58
- jest.mock('@reltio/mdm-sdk', function () { return (__assign(__assign({}, jest.requireActual('@reltio/mdm-sdk')), { getTransitiveMatches: jest.fn(), getMatchesForDataTenantEntity: jest.fn(), isDataTenantEntity: jest.fn().mockImplementation(function (entity) { return !!entity.dataTenant; }) })); });
58
+ jest.mock('@reltio/mdm-sdk', function () { return (__assign(__assign({}, jest.requireActual('@reltio/mdm-sdk')), { getTransitiveMatches: jest.fn(), getMatchesForDataTenantEntity: jest.fn(), getDataTenantMatchGroups: jest.fn(), isDataTenantEntity: jest.fn().mockImplementation(function (entity) { return !!entity.dataTenant; }) })); });
59
59
  /**
60
60
  * useMatchesLoader runs a useEffect that calls getMatches() on mount. getMatches() is async:
61
61
  * it calls the SDK (getTransitiveMatches / getMatchesForDataTenantEntity), and when the
@@ -192,4 +192,47 @@ describe('Test useMatchesLoader hook', function () {
192
192
  }
193
193
  });
194
194
  }); });
195
+ it('should add data tenant match rule labels to DTSS matches', function () { return __awaiter(void 0, void 0, void 0, function () {
196
+ var matchRuleUri, result;
197
+ var _a;
198
+ return __generator(this, function (_b) {
199
+ switch (_b.label) {
200
+ case 0:
201
+ matchRuleUri = 'configuration/entityTypes/HCP/matchGroups/DataTenantRule';
202
+ mdm_sdk_1.getMatchesForDataTenantEntity.mockResolvedValueOnce([
203
+ {
204
+ object: {
205
+ uri: 'entities/customerTenantMatch',
206
+ type: 'configuration/entityTypes/HCP'
207
+ },
208
+ matchRules: [matchRuleUri]
209
+ }
210
+ ]);
211
+ mdm_sdk_1.getDataTenantMatchGroups.mockResolvedValueOnce((_a = {},
212
+ _a[matchRuleUri] = 'Data Tenant Match Rule',
213
+ _a));
214
+ result = setUp({
215
+ props: __assign(__assign({}, defaultProps), { entity: __assign(__assign({}, defaultProps.entity), { dataTenant: 'tenantId4' }) }),
216
+ mdmValues: __assign(__assign({}, defaultMdmValues), { dtssPath: 'dtssPath/', tenant: { id: 'customerTenant', name: 'customerTenant' } })
217
+ }).result;
218
+ return [4 /*yield*/, (0, react_2.waitFor)(function () {
219
+ var _a;
220
+ expect(result.current.matches).toEqual([
221
+ expect.objectContaining({
222
+ matchRuleLabels: (_a = {},
223
+ _a[matchRuleUri] = 'Data Tenant Match Rule',
224
+ _a)
225
+ })
226
+ ]);
227
+ })];
228
+ case 1:
229
+ _b.sent();
230
+ expect(mdm_sdk_1.getDataTenantMatchGroups).toHaveBeenCalledWith({
231
+ dtssPath: 'dtssPath/',
232
+ customerTenant: 'customerTenant'
233
+ });
234
+ return [2 /*return*/];
235
+ }
236
+ });
237
+ }); });
195
238
  });
@@ -17,7 +17,7 @@ import { useHtmlSanitizer } from '../useHtmlSanitizer';
17
17
  import { InterceptHandlersContext } from '../../contexts/InterceptHandlersContext';
18
18
  import { SandboxAPIContext } from '../../contexts/SandboxAPIContext';
19
19
  import { SnackbarContext } from '../../contexts/SnackbarContext';
20
- import { useMdmAction, useMdmApiPath, useMdmEntity, useMdmEntityUri, useMdmIsEditableMode, useMdmMetadata, useMdmReltioPath, useMdmSearchProviderData, useMdmServicesPath, useMdmTenant, useMdmUiPath, useMdmUser, useMdmWorkflowEnvironmentUrl, useMdmWorkflowPath } from '../../contexts/MdmModuleContext';
20
+ import { useMdmAction, useMdmApiPath, useMdmEntity, useMdmIsEditableMode, useMdmMetadata, useMdmReltioPath, useMdmSearchProviderData, useMdmServicesPath, useMdmTenant, useMdmUiPath, useMdmUser, useMdmWorkflowEnvironmentUrl, useMdmWorkflowPath } from '../../contexts/MdmModuleContext';
21
21
  import { processRequest } from './API';
22
22
  import { getWorkerURL, isValidCustomScript, resetHtml, getProcessedTask, setUpRequestInterceptor, setUpResponseInterceptor } from '../../helpers/customScript';
23
23
  export var useAPI = function (config) {
@@ -33,6 +33,7 @@ export var useAPI = function (config) {
33
33
  var _j = useState({}), customStyles = _j[0], setCustomStyles = _j[1];
34
34
  var workerRef = useRef(null);
35
35
  var entityRef = useRef(null);
36
+ var searchQueryRef = useRef(null);
36
37
  var requestListeners = useRef({});
37
38
  var responseListeners = useRef({});
38
39
  var sanitizeHtml = useHtmlSanitizer();
@@ -51,7 +52,6 @@ export var useAPI = function (config) {
51
52
  var reltioPath = useMdmReltioPath();
52
53
  var workflowPath = useMdmWorkflowPath();
53
54
  var user = useMdmUser();
54
- var entityUri = useMdmEntityUri();
55
55
  var isEditableMode = useMdmIsEditableMode();
56
56
  var environment = useMdmWorkflowEnvironmentUrl();
57
57
  var searchProviderData = useMdmSearchProviderData();
@@ -59,6 +59,7 @@ export var useAPI = function (config) {
59
59
  var innerText = '';
60
60
  var popupText = '';
61
61
  var process = function (_a) {
62
+ var _b;
62
63
  var task = _a.task, worker = _a.worker;
63
64
  if (task) {
64
65
  switch (task.action) {
@@ -144,11 +145,11 @@ export var useAPI = function (config) {
144
145
  }
145
146
  case CustomAction.REQUEST: {
146
147
  var processedTask = getProcessedTask(task, apiPath, reltioPath, tenant);
147
- processRequest(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, servicesPath: servicesPath, apiPath: reltioPath, tenant: tenant, workflowPath: workflowPath, entity: entityRef.current, entityUri: entityUri, environment: environment, query: searchQuery, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
148
+ processRequest(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, servicesPath: servicesPath, apiPath: reltioPath, tenant: tenant, workflowPath: workflowPath, entity: entityRef.current, entityUri: (_b = entityRef.current) === null || _b === void 0 ? void 0 : _b.uri, environment: environment, query: searchQueryRef.current, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
148
149
  break;
149
150
  }
150
151
  case CustomAction.RETURN_PROCESSED_API_RESPONSE: {
151
- var _b = task.params, id = _b.id, data = _b.data;
152
+ var _c = task.params, id = _c.id, data = _c.data;
152
153
  if (responseListeners.current[id]) {
153
154
  responseListeners.current[id].next(data);
154
155
  responseListeners.current = omit([id], responseListeners.current);
@@ -156,7 +157,7 @@ export var useAPI = function (config) {
156
157
  break;
157
158
  }
158
159
  case CustomAction.RETURN_PROCESSED_API_REQUEST: {
159
- var _c = task.params, id = _c.id, data = _c.data;
160
+ var _d = task.params, id = _d.id, data = _d.data;
160
161
  if (requestListeners.current[id]) {
161
162
  requestListeners.current[id].resolve(data);
162
163
  requestListeners.current = omit([id], requestListeners.current);
@@ -221,6 +222,7 @@ export var useAPI = function (config) {
221
222
  postEventMessage('editMode', isEditableMode);
222
223
  }, [isEditableMode]);
223
224
  useEffect(function () {
225
+ searchQueryRef.current = searchQuery;
224
226
  postEventMessage('changeSearchQuery', searchQuery);
225
227
  }, [searchQuery]);
226
228
  useEffect(function () {
@@ -1015,6 +1015,37 @@ describe('useAPI custom sandbox behavior', function () {
1015
1015
  });
1016
1016
  expect(postMessage).toHaveBeenCalledWith(__assign({ action: 'response', result: 'entities/mnGg0XB' }, params));
1017
1017
  });
1018
+ it('should return the current entityUri on getEntityUri after navigating to another entity', function () {
1019
+ var updateMdmValues = setUp().updateMdmValues;
1020
+ act(function () {
1021
+ onmessageFromWorker({
1022
+ action: CustomAction.REQUEST,
1023
+ params: { name: RequestAction.GET_ENTITY_URI, id: 1 }
1024
+ });
1025
+ });
1026
+ expect(postMessage).toHaveBeenLastCalledWith({
1027
+ action: 'response',
1028
+ result: 'entities/mnGg0XB',
1029
+ name: RequestAction.GET_ENTITY_URI,
1030
+ id: 1
1031
+ });
1032
+ var newEntity = { uri: 'entities/NEW999', type: 'configuration/entityTypes/HCA', attributes: {} };
1033
+ act(function () {
1034
+ updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { entity: newEntity })); });
1035
+ });
1036
+ act(function () {
1037
+ onmessageFromWorker({
1038
+ action: CustomAction.REQUEST,
1039
+ params: { name: RequestAction.GET_ENTITY_URI, id: 2 }
1040
+ });
1041
+ });
1042
+ expect(postMessage).toHaveBeenLastCalledWith({
1043
+ action: 'response',
1044
+ result: 'entities/NEW999',
1045
+ name: RequestAction.GET_ENTITY_URI,
1046
+ id: 2
1047
+ });
1048
+ });
1018
1049
  it('should return entity on getEntity request', function () {
1019
1050
  setUp();
1020
1051
  var params = {
@@ -37,7 +37,7 @@ import { useWorkflowCheckPermission } from '../../features/workflow';
37
37
  import { SnackbarContext } from '../../contexts/SnackbarContext';
38
38
  import { SandboxAPIContext } from '../../contexts/SandboxAPIContext';
39
39
  import { InterceptHandlersContext } from '../../contexts/InterceptHandlersContext';
40
- import { useMdmAction, useMdmApiPath, useMdmEntity, useMdmEntityUri, useMdmMetadata, useMdmReltioPath, useMdmSearchProviderData, useMdmServicesPath, useMdmTenant, useMdmUiPath, useMdmUser, useMdmWorkflowEnvironmentUrl, useMdmWorkflowPath } from '../../contexts/MdmModuleContext';
40
+ import { useMdmAction, useMdmApiPath, useMdmEntity, useMdmMetadata, useMdmReltioPath, useMdmSearchProviderData, useMdmServicesPath, useMdmTenant, useMdmUiPath, useMdmUser, useMdmWorkflowEnvironmentUrl, useMdmWorkflowPath } from '../../contexts/MdmModuleContext';
41
41
  import { processRequest } from '../useAPI';
42
42
  import { isValidCustomScript, isURLtoProcess, getProcessedTask, getRequestFakeUrl, getResponseFakeUrl, getWorkerURL } from '../../helpers/customScript';
43
43
  var getCustomViewConfig = function (config) {
@@ -66,7 +66,6 @@ export var useCustomScripts = function (config) {
66
66
  var apiPath = useMdmApiPath();
67
67
  var workflowPath = useMdmWorkflowPath();
68
68
  var user = useMdmUser();
69
- var entityUri = useMdmEntityUri();
70
69
  var environment = useMdmWorkflowEnvironmentUrl();
71
70
  var workflowCheckPermission = useWorkflowCheckPermission();
72
71
  var searchProviderData = useMdmSearchProviderData();
@@ -74,7 +73,10 @@ export var useCustomScripts = function (config) {
74
73
  var requestListeners = useRef({});
75
74
  var responseListeners = useRef({});
76
75
  var workers = useRef({});
76
+ var entityRef = useRef(null);
77
+ var searchQueryRef = useRef(null);
77
78
  var processMessageFromWorker = function (_a) {
79
+ var _b;
78
80
  var task = _a.task, worker = _a.worker, config = _a.config;
79
81
  if (task) {
80
82
  switch (task.action) {
@@ -88,11 +90,11 @@ export var useCustomScripts = function (config) {
88
90
  }
89
91
  case CustomAction.REQUEST: {
90
92
  var processedTask = getProcessedTask(task, apiPath, reltioPath, tenant);
91
- processRequest(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, apiPath: reltioPath, servicesPath: servicesPath, tenant: tenant, workflowPath: workflowPath, entity: entity, entityUri: entityUri, environment: environment, query: searchQuery, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
93
+ processRequest(__assign({ params: processedTask.params, permissions: config.action.permissions, worker: worker, metadata: metadata, config: config, user: user, apiPath: reltioPath, servicesPath: servicesPath, tenant: tenant, workflowPath: workflowPath, entity: entityRef.current, entityUri: (_b = entityRef.current) === null || _b === void 0 ? void 0 : _b.uri, environment: environment, query: searchQueryRef.current, openSearch: openSearch, workflowCheckPermission: workflowCheckPermission }, qxApi));
92
94
  break;
93
95
  }
94
96
  case CustomAction.RETURN_PROCESSED_API_RESPONSE: {
95
- var _b = task.params, id = _b.id, data = _b.data;
97
+ var _c = task.params, id = _c.id, data = _c.data;
96
98
  if (responseListeners.current[id]) {
97
99
  responseListeners.current[id].next(data);
98
100
  responseListeners.current = omit([id], responseListeners.current);
@@ -100,7 +102,7 @@ export var useCustomScripts = function (config) {
100
102
  break;
101
103
  }
102
104
  case CustomAction.RETURN_PROCESSED_API_REQUEST: {
103
- var _c = task.params, id = _c.id, data = _c.data;
105
+ var _d = task.params, id = _d.id, data = _d.data;
104
106
  if (requestListeners.current[id]) {
105
107
  requestListeners.current[id].resolve(data);
106
108
  requestListeners.current = omit([id], requestListeners.current);
@@ -124,6 +126,12 @@ export var useCustomScripts = function (config) {
124
126
  };
125
127
  }
126
128
  }, [config, metadata]);
129
+ useEffect(function () {
130
+ entityRef.current = entity;
131
+ }, [entity]);
132
+ useEffect(function () {
133
+ searchQueryRef.current = searchQuery;
134
+ }, [searchQuery]);
127
135
  var customScriptRequestInterceptor = function (_a) {
128
136
  var _b;
129
137
  var url = _a.url, next = _a.next, resolve = _a.resolve, _c = _a.options, options = _c === void 0 ? {} : _c;
@@ -94,13 +94,16 @@ describe('useCustomScripts', function () {
94
94
  };
95
95
  var setUp = function (_a) {
96
96
  var _b = _a === void 0 ? {} : _a, _c = _b.mdmValues, mdmValues = _c === void 0 ? defaultMdmValues : _c, _d = _b.config, config = _d === void 0 ? createConfig() : _d;
97
+ var updateMdmValues;
97
98
  var Providers = function (_a) {
98
99
  var children = _a.children;
99
- return (React.createElement(MdmModuleProvider, { values: mdmValues },
100
+ var _b = React.useState(mdmValues), mdmValuesState = _b[0], setMdmValuesState = _b[1];
101
+ updateMdmValues = setMdmValuesState;
102
+ return (React.createElement(MdmModuleProvider, { values: mdmValuesState },
100
103
  React.createElement(SnackbarContext.Provider, { value: showSnackbarMessage },
101
104
  React.createElement(InterceptHandlersContext.Provider, { value: interceptors }, children))));
102
105
  };
103
- return renderHook(useCustomScripts, { initialProps: config, wrapper: Providers });
106
+ return __assign(__assign({}, renderHook(useCustomScripts, { initialProps: config, wrapper: Providers })), { updateMdmValues: updateMdmValues });
104
107
  };
105
108
  var postMessage = jest.fn();
106
109
  var onmessageFromWorker;
@@ -460,4 +463,77 @@ describe('useCustomScripts', function () {
460
463
  }
461
464
  });
462
465
  }); });
466
+ it('should resolve getEntityUri/getEntity with the current entity after navigation', function () {
467
+ var entityA = { uri: 'entities/AAA', type: 'configuration/entityTypes/HCA', attributes: {} };
468
+ var entityB = { uri: 'entities/BBB', type: 'configuration/entityTypes/HCA', attributes: {} };
469
+ var updateMdmValues = setUp({ mdmValues: __assign(__assign({}, defaultMdmValues), { entity: entityA }) }).updateMdmValues;
470
+ act(function () {
471
+ onmessageFromWorker({
472
+ action: CustomAction.REQUEST,
473
+ params: { name: RequestAction.GET_ENTITY_URI, id: 'id1' }
474
+ });
475
+ });
476
+ expect(postMessage).toHaveBeenLastCalledWith({
477
+ action: 'response',
478
+ result: 'entities/AAA',
479
+ name: RequestAction.GET_ENTITY_URI,
480
+ id: 'id1'
481
+ });
482
+ act(function () {
483
+ updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { entity: entityB })); });
484
+ });
485
+ act(function () {
486
+ onmessageFromWorker({
487
+ action: CustomAction.REQUEST,
488
+ params: { name: RequestAction.GET_ENTITY_URI, id: 'id2' }
489
+ });
490
+ });
491
+ expect(postMessage).toHaveBeenLastCalledWith({
492
+ action: 'response',
493
+ result: 'entities/BBB',
494
+ name: RequestAction.GET_ENTITY_URI,
495
+ id: 'id2'
496
+ });
497
+ act(function () {
498
+ onmessageFromWorker({ action: CustomAction.REQUEST, params: { name: RequestAction.GET_ENTITY, id: 'id3' } });
499
+ });
500
+ expect(postMessage).toHaveBeenLastCalledWith({
501
+ action: 'response',
502
+ result: entityB,
503
+ name: RequestAction.GET_ENTITY,
504
+ id: 'id3'
505
+ });
506
+ });
507
+ it('should resolve getSearchQuery with the current query after it changes', function () {
508
+ var updateMdmValues = setUp({
509
+ mdmValues: __assign(__assign({}, defaultMdmValues), { searchProviderData: { type: 'search', data: { query: 'q1' } } })
510
+ }).updateMdmValues;
511
+ act(function () {
512
+ onmessageFromWorker({
513
+ action: CustomAction.REQUEST,
514
+ params: { name: RequestAction.GET_SEARCH_QUERY, id: 'id1' }
515
+ });
516
+ });
517
+ expect(postMessage).toHaveBeenLastCalledWith({
518
+ action: 'response',
519
+ result: 'q1',
520
+ name: RequestAction.GET_SEARCH_QUERY,
521
+ id: 'id1'
522
+ });
523
+ act(function () {
524
+ updateMdmValues(function (prev) { return (__assign(__assign({}, prev), { searchProviderData: { type: 'search', data: { query: 'q2' } } })); });
525
+ });
526
+ act(function () {
527
+ onmessageFromWorker({
528
+ action: CustomAction.REQUEST,
529
+ params: { name: RequestAction.GET_SEARCH_QUERY, id: 'id2' }
530
+ });
531
+ });
532
+ expect(postMessage).toHaveBeenLastCalledWith({
533
+ action: 'response',
534
+ result: 'q2',
535
+ name: RequestAction.GET_SEARCH_QUERY,
536
+ id: 'id2'
537
+ });
538
+ });
463
539
  });
@@ -0,0 +1,4 @@
1
+ import { Match } from '@reltio/mdm-sdk';
2
+ export type MatchWithRuleLabels = Match & {
3
+ matchRuleLabels?: Record<string, string>;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
- import { Entity, Match, SortingField, EntitiesMap } from '@reltio/mdm-sdk';
1
+ import { Entity, SortingField, EntitiesMap } from '@reltio/mdm-sdk';
2
+ import type { MatchWithRuleLabels } from './types';
2
3
  type Props = {
3
4
  enabled: boolean;
4
5
  entity: Entity;
@@ -17,7 +18,7 @@ type Props = {
17
18
  };
18
19
  export declare const useMatchesLoader: ({ enabled, entity, filter, rules, onFinishLoading, onStartLoading, options, page, rowsPerPage, sorting, markMatchedValues }: Props) => {
19
20
  total: number;
20
- matches: Match[];
21
+ matches: MatchWithRuleLabels[];
21
22
  entitiesMap: EntitiesMap<Pick<Entity, "label" | "type" | "uri">>;
22
23
  loadMatches: () => void;
23
24
  };
@@ -10,22 +10,52 @@ var __assign = (this && this.__assign) || function () {
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
12
  import { useCallback, useEffect, useMemo, useState } from 'react';
13
- import { ActivityFilter, collectAllTransitiveEntitiesUris, getEntitiesMapForTransitiveMatches, getMatchesForDataTenantEntity, getMatchesFromDataTenants, getTransitiveMatches, isDataTenantEntity, isTempUri, withDtssPotentialItems, addGlobalFilterToQuery, addGetTransitiveMatchesMaskingOptions } from '@reltio/mdm-sdk';
13
+ import { ActivityFilter, collectAllTransitiveEntitiesUris, getEntitiesMapForTransitiveMatches, getDataTenantMatchGroups, getMatchesForDataTenantEntity, getMatchesFromDataTenants, getTransitiveMatches, isDataTenantEntity, isTempUri, withDtssPotentialItems, addGlobalFilterToQuery, addGetTransitiveMatchesMaskingOptions } from '@reltio/mdm-sdk';
14
14
  import { usePagingSimulator } from '../usePagingSimulator';
15
15
  import { useSafePromise } from '../useSafePromise';
16
16
  import { useMdmDataTenantsWithIdBuilder, useMdmDtssPath, useMdmGlobalSearchRequestOptions, useMdmMetadata, useMdmTenant, useMdmShowMasking } from '../../contexts/MdmModuleContext';
17
+ var matchRuleLabelsCache = new Map();
18
+ // Match rule labels are static per (dtssPath, customerTenant), so the request is
19
+ // cached and reused across reloads (paging, filtering, sorting) instead of being
20
+ // refetched every time matches are loaded. Failed requests are evicted so a later
21
+ // load can retry.
22
+ var getCachedDataTenantMatchRuleLabels = function (dtssPath, customerTenant) {
23
+ var cacheKey = "".concat(dtssPath, "::").concat(customerTenant);
24
+ var cachedRequest = matchRuleLabelsCache.get(cacheKey);
25
+ if (cachedRequest !== undefined) {
26
+ return cachedRequest;
27
+ }
28
+ var request = getDataTenantMatchGroups({ dtssPath: dtssPath, customerTenant: customerTenant }).catch(function () {
29
+ matchRuleLabelsCache.delete(cacheKey);
30
+ return {};
31
+ });
32
+ matchRuleLabelsCache.set(cacheKey, request);
33
+ return request;
34
+ };
35
+ var addDataTenantMatchRuleLabels = function (matches, entity, matchRuleLabels) {
36
+ if (!Object.keys(matchRuleLabels).length) {
37
+ return matches;
38
+ }
39
+ return matches.map(function (match) {
40
+ var _a;
41
+ return (isDataTenantEntity(entity) || isDataTenantEntity(match.object)) &&
42
+ ((_a = match.matchRules) === null || _a === void 0 ? void 0 : _a.some(function (matchRuleUri) { return matchRuleLabels[matchRuleUri]; }))
43
+ ? __assign(__assign({}, match), { matchRuleLabels: matchRuleLabels }) : match;
44
+ });
45
+ };
17
46
  export var useMatchesLoader = function (_a) {
18
47
  var enabled = _a.enabled, entity = _a.entity, filter = _a.filter, rules = _a.rules, onFinishLoading = _a.onFinishLoading, onStartLoading = _a.onStartLoading, options = _a.options, page = _a.page, rowsPerPage = _a.rowsPerPage, sorting = _a.sorting, markMatchedValues = _a.markMatchedValues;
19
48
  var _b = sorting || {}, order = _b.order, field = _b.field;
20
49
  var _c = useState(0), total = _c[0], setTotal = _c[1];
21
50
  var _d = useState([]), matches = _d[0], setMatches = _d[1];
22
- var _e = useState({}), entitiesMap = _e[0], setEntitiesMap = _e[1];
51
+ var _e = useState({}), matchRuleLabels = _e[0], setMatchRuleLabels = _e[1];
52
+ var _f = useState({}), entitiesMap = _f[0], setEntitiesMap = _f[1];
23
53
  var metadata = useMdmMetadata();
24
54
  var tenant = useMdmTenant();
25
55
  var dtssPath = useMdmDtssPath();
26
56
  var dataTenants = useMdmDataTenantsWithIdBuilder();
27
57
  var showMasking = useMdmShowMasking();
28
- var _f = useMdmGlobalSearchRequestOptions() || {}, activityFilter = _f.activityFilter, globalFilter = _f.globalFilter;
58
+ var _g = useMdmGlobalSearchRequestOptions() || {}, activityFilter = _g.activityFilter, globalFilter = _g.globalFilter;
29
59
  var activeness = typeof options.showInactiveEntities === 'boolean'
30
60
  ? options.showInactiveEntities
31
61
  ? ActivityFilter.ALL
@@ -48,6 +78,7 @@ export var useMatchesLoader = function (_a) {
48
78
  var getTransitiveMatchesWithPaging = usePagingSimulator(getTransitiveMatchesWithDtss);
49
79
  var getPagedMatchesForDataTenantEntity = usePagingSimulator(getMatchesForDataTenantEntity);
50
80
  var safePromise = useSafePromise();
81
+ var safeMatchRuleLabelsPromise = useSafePromise();
51
82
  var getMatches = useCallback(function (force) {
52
83
  if (force === void 0) { force = false; }
53
84
  if (!enabled || isTempUri(entityUri)) {
@@ -57,7 +88,7 @@ export var useMatchesLoader = function (_a) {
57
88
  return;
58
89
  }
59
90
  onStartLoading === null || onStartLoading === void 0 ? void 0 : onStartLoading();
60
- safePromise(isDataTenantEntity(entity)
91
+ var matchesRequest = isDataTenantEntity(entity)
61
92
  ? getPagedMatchesForDataTenantEntity({
62
93
  dtssPath: dtssPath,
63
94
  entity: entity,
@@ -77,7 +108,8 @@ export var useMatchesLoader = function (_a) {
77
108
  markMatchedValues: markMatchedValues,
78
109
  deep: options.showTransitiveMatches ? undefined : 1,
79
110
  activeness: activeness
80
- }, showMasking)), { offset: page * rowsPerPage, force: force })))
111
+ }, showMasking)), { offset: page * rowsPerPage, force: force }));
112
+ safePromise(matchesRequest)
81
113
  .then(function (result) {
82
114
  var response = result.response, total = result.total, originalResponse = result.originalResponse;
83
115
  setTotal(total);
@@ -123,12 +155,27 @@ export var useMatchesLoader = function (_a) {
123
155
  useEffect(function () {
124
156
  getMatches();
125
157
  }, [getMatches]);
158
+ // Match rule labels are loaded independently of the matches request so the
159
+ // matches list renders without waiting for them; they are applied as a
160
+ // derived layer once available. Keyed on a stable boolean rather than the
161
+ // dataTenants array reference to avoid re-firing on every render.
162
+ var hasDataTenants = !!(dataTenants === null || dataTenants === void 0 ? void 0 : dataTenants.length);
163
+ useEffect(function () {
164
+ if (!dtssPath || !tenant || !hasDataTenants) {
165
+ setMatchRuleLabels({});
166
+ return;
167
+ }
168
+ safeMatchRuleLabelsPromise(getCachedDataTenantMatchRuleLabels(dtssPath, tenant))
169
+ .then(setMatchRuleLabels)
170
+ .catch(function () { return setMatchRuleLabels({}); });
171
+ }, [dtssPath, tenant, hasDataTenants, safeMatchRuleLabelsPromise]);
172
+ var matchesWithRuleLabels = useMemo(function () { return addDataTenantMatchRuleLabels(matches, entity, matchRuleLabels); }, [matches, entity, matchRuleLabels]);
126
173
  var loadMatches = useCallback(function () {
127
174
  getMatches(true);
128
175
  }, [getMatches]);
129
176
  return {
130
177
  total: total,
131
- matches: matches,
178
+ matches: matchesWithRuleLabels,
132
179
  entitiesMap: entitiesMap,
133
180
  loadMatches: loadMatches
134
181
  };
@@ -46,11 +46,11 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import React from 'react';
49
- import { getTransitiveMatches, getMatchesForDataTenantEntity } from '@reltio/mdm-sdk';
49
+ import { getTransitiveMatches, getMatchesForDataTenantEntity, getDataTenantMatchGroups } from '@reltio/mdm-sdk';
50
50
  import { renderHook, waitFor } from '@testing-library/react';
51
51
  import { MdmModuleProvider } from '../../contexts/MdmModuleContext';
52
52
  import { useMatchesLoader } from './useMatchesLoader';
53
- jest.mock('@reltio/mdm-sdk', function () { return (__assign(__assign({}, jest.requireActual('@reltio/mdm-sdk')), { getTransitiveMatches: jest.fn(), getMatchesForDataTenantEntity: jest.fn(), isDataTenantEntity: jest.fn().mockImplementation(function (entity) { return !!entity.dataTenant; }) })); });
53
+ jest.mock('@reltio/mdm-sdk', function () { return (__assign(__assign({}, jest.requireActual('@reltio/mdm-sdk')), { getTransitiveMatches: jest.fn(), getMatchesForDataTenantEntity: jest.fn(), getDataTenantMatchGroups: jest.fn(), isDataTenantEntity: jest.fn().mockImplementation(function (entity) { return !!entity.dataTenant; }) })); });
54
54
  /**
55
55
  * useMatchesLoader runs a useEffect that calls getMatches() on mount. getMatches() is async:
56
56
  * it calls the SDK (getTransitiveMatches / getMatchesForDataTenantEntity), and when the
@@ -187,4 +187,47 @@ describe('Test useMatchesLoader hook', function () {
187
187
  }
188
188
  });
189
189
  }); });
190
+ it('should add data tenant match rule labels to DTSS matches', function () { return __awaiter(void 0, void 0, void 0, function () {
191
+ var matchRuleUri, result;
192
+ var _a;
193
+ return __generator(this, function (_b) {
194
+ switch (_b.label) {
195
+ case 0:
196
+ matchRuleUri = 'configuration/entityTypes/HCP/matchGroups/DataTenantRule';
197
+ getMatchesForDataTenantEntity.mockResolvedValueOnce([
198
+ {
199
+ object: {
200
+ uri: 'entities/customerTenantMatch',
201
+ type: 'configuration/entityTypes/HCP'
202
+ },
203
+ matchRules: [matchRuleUri]
204
+ }
205
+ ]);
206
+ getDataTenantMatchGroups.mockResolvedValueOnce((_a = {},
207
+ _a[matchRuleUri] = 'Data Tenant Match Rule',
208
+ _a));
209
+ result = setUp({
210
+ props: __assign(__assign({}, defaultProps), { entity: __assign(__assign({}, defaultProps.entity), { dataTenant: 'tenantId4' }) }),
211
+ mdmValues: __assign(__assign({}, defaultMdmValues), { dtssPath: 'dtssPath/', tenant: { id: 'customerTenant', name: 'customerTenant' } })
212
+ }).result;
213
+ return [4 /*yield*/, waitFor(function () {
214
+ var _a;
215
+ expect(result.current.matches).toEqual([
216
+ expect.objectContaining({
217
+ matchRuleLabels: (_a = {},
218
+ _a[matchRuleUri] = 'Data Tenant Match Rule',
219
+ _a)
220
+ })
221
+ ]);
222
+ })];
223
+ case 1:
224
+ _b.sent();
225
+ expect(getDataTenantMatchGroups).toHaveBeenCalledWith({
226
+ dtssPath: 'dtssPath/',
227
+ customerTenant: 'customerTenant'
228
+ });
229
+ return [2 /*return*/];
230
+ }
231
+ });
232
+ }); });
190
233
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reltio/components",
3
- "version": "1.4.2276",
3
+ "version": "1.4.2278",
4
4
  "license": "SEE LICENSE IN LICENSE FILE",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./index.js",
@@ -11,7 +11,7 @@
11
11
  "@fluentui/react-context-selector": "^9.1.26",
12
12
  "@googlemaps/markerclusterer": "^2.5.3",
13
13
  "@react-sigma/core": "3.4.0",
14
- "@reltio/mdm-sdk": "^1.4.2052",
14
+ "@reltio/mdm-sdk": "^1.4.2053",
15
15
  "@vis.gl/react-google-maps": "^1.3.0",
16
16
  "d3-cloud": "^1.2.5",
17
17
  "d3-geo": "^2.0.1",