@truedat/dq 4.56.8 → 4.58.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.56.9] 2022-11-12
4
+
5
+ ### Added
6
+
7
+ - [TD-5161] Create link with concepts once the implementations are created
8
+
3
9
  ## [4.56.7] 2022-11-28
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "4.56.8",
3
+ "version": "4.58.0",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -34,7 +34,7 @@
34
34
  "@testing-library/jest-dom": "^5.16.5",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "4.56.8",
37
+ "@truedat/test": "4.58.0",
38
38
  "babel-jest": "^28.1.0",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
@@ -92,8 +92,8 @@
92
92
  },
93
93
  "dependencies": {
94
94
  "@apollo/client": "^3.7.1",
95
- "@truedat/core": "4.56.8",
96
- "@truedat/df": "4.56.8",
95
+ "@truedat/core": "4.58.0",
96
+ "@truedat/df": "4.58.0",
97
97
  "graphql": "^15.5.3",
98
98
  "moment": "^2.29.4",
99
99
  "path-to-regexp": "^1.7.0",
@@ -108,12 +108,13 @@
108
108
  "redux-saga-routines": "^3.2.3",
109
109
  "reselect": "^4.1.7",
110
110
  "semantic-ui-calendar-react": "^0.15.3",
111
- "semantic-ui-react": "^2.1.4"
111
+ "semantic-ui-react": "^2.1.4",
112
+ "swr": "^1.3.0"
112
113
  },
113
114
  "peerDependencies": {
114
115
  "react": ">= 16.8.6 < 17",
115
116
  "react-dom": ">= 16.8.6 < 17",
116
117
  "semantic-ui-react": ">= 2.0.3 < 2.2"
117
118
  },
118
- "gitHead": "9bf266eb26e4af0b774560d5a88b0f22325495e0"
119
+ "gitHead": "3987557a127b4e999021e0d7179ad7a6375bfe01"
119
120
  }
@@ -1,12 +1,16 @@
1
1
  import _ from "lodash/fp";
2
2
  import React, { useState } from "react";
3
3
  import PropTypes from "prop-types";
4
+ import queryString from "query-string";
4
5
  import { connect } from "react-redux";
5
- import { Divider, Header, Grid } from "semantic-ui-react";
6
6
  import { FormattedMessage } from "react-intl";
7
+ import { useLocation } from "react-router-dom";
8
+ import { Divider, Loader, Header, Grid } from "semantic-ui-react";
9
+ import { linkTo } from "@truedat/core/routes";
7
10
  import { Loading } from "@truedat/core/components";
8
11
  import { useOperators } from "@truedat/core/hooks";
9
12
  import { getFieldType } from "@truedat/core/services/fieldType";
13
+ import { useConcept } from "../hooks/useConcept";
10
14
  import {
11
15
  createRuleImplementation,
12
16
  updateRuleImplementation,
@@ -245,6 +249,10 @@ const withConditionDefaultAlias = (conditions, structures) =>
245
249
  })(conditions);
246
250
 
247
251
  export const NewRuleImplementation = ({
252
+ conceptDomainId,
253
+ conceptDomainIds,
254
+ conceptId,
255
+ conceptVersionId,
248
256
  clone = false,
249
257
  createRuleImplementation,
250
258
  updateRuleImplementation,
@@ -353,7 +361,7 @@ export const NewRuleImplementation = ({
353
361
  minimum: null,
354
362
  goal: null,
355
363
  rule_id: _.propOr(null, "id")(rule),
356
- domain_id: _.propOr(null, "domain_id")(rule),
364
+ domain_id: conceptDomainId || rule?.domain_id || null,
357
365
  rule: rule,
358
366
  }
359
367
  );
@@ -536,6 +544,13 @@ export const NewRuleImplementation = ({
536
544
  clone || !edition
537
545
  ? createRuleImplementation({
538
546
  rule_implementation,
547
+ redirectUrl: conceptId
548
+ ? linkTo.CONCEPT_LINKS_IMPLEMENTATIONS({
549
+ business_concept_id: conceptId,
550
+ id: conceptVersionId,
551
+ })
552
+ : null,
553
+ conceptId,
539
554
  })
540
555
  : updateRuleImplementation({
541
556
  rule_implementation: {
@@ -569,6 +584,8 @@ export const NewRuleImplementation = ({
569
584
  <RuleImplementationRawForm
570
585
  mode={clone ? "clone" : edition ? "edition" : ""}
571
586
  implementationKey={ruleImplementation.implementationKey}
587
+ conceptDomainId={conceptDomainId}
588
+ domainIds={conceptDomainIds}
572
589
  rawContent={ruleImplementation.rawContent}
573
590
  setImplementationKey={setImplementationKey}
574
591
  setImplementationRawContent={setImplementationRawContent}
@@ -581,6 +598,8 @@ export const NewRuleImplementation = ({
581
598
  addPopulation={addPopulation}
582
599
  addValidation={addValidation}
583
600
  addSegment={addSegment}
601
+ conceptDomainId={conceptDomainId}
602
+ domainIds={conceptDomainIds}
584
603
  onSubmit={doSubmit}
585
604
  onChange={onChange}
586
605
  operators={operators}
@@ -604,6 +623,8 @@ export const NewRuleImplementation = ({
604
623
  };
605
624
 
606
625
  NewRuleImplementation.propTypes = {
626
+ conceptId: PropTypes.number,
627
+ conceptVersionId: PropTypes.number,
607
628
  clone: PropTypes.bool,
608
629
  createRuleImplementation: PropTypes.func.isRequired,
609
630
  edition: PropTypes.bool,
@@ -613,6 +634,8 @@ NewRuleImplementation.propTypes = {
613
634
  structuresFields: PropTypes.object,
614
635
  structuresSiblings: PropTypes.object,
615
636
  updateRuleImplementation: PropTypes.func,
637
+ conceptDomainId: PropTypes.number,
638
+ conceptDomainIds: PropTypes.array,
616
639
  };
617
640
 
618
641
  const mapStateToProps = (state) => ({
@@ -626,13 +649,54 @@ const mapStateToProps = (state) => ({
626
649
  structuresSiblings: state.structuresSiblings,
627
650
  });
628
651
 
629
- export const ImplementationLoader = (props) => {
652
+ export const ImplementationOperatorLoader = (props) => {
630
653
  const { data, error, loading } = useOperators();
631
654
  if (error) return null;
632
655
  if (loading) return <Loading />;
633
656
  return <NewRuleImplementation operators={data} {...props} />;
634
657
  };
635
658
 
659
+ export const ImplementationConceptLoader = (props) => {
660
+ const conceptId = props.conceptId;
661
+ const conceptVersionId = props.conceptVersionId;
662
+
663
+ const { concept, error, loading } = useConcept(conceptId, conceptVersionId);
664
+ // TODO: if error??;
665
+ const domainId = concept?.data?.domain?.id;
666
+ const sharedTo = concept?.data?._embedded?.shared_to;
667
+
668
+ const conceptDomainIds = _.flow(
669
+ _.map(({ id: id }) => id),
670
+ _.concat(domainId)
671
+ )(sharedTo);
672
+
673
+ return loading ? (
674
+ <Loader />
675
+ ) : (
676
+ <ImplementationOperatorLoader
677
+ {...props}
678
+ conceptDomainId={domainId}
679
+ conceptDomainIds={conceptDomainIds}
680
+ conceptId={conceptId}
681
+ conceptVersionId={conceptVersionId}
682
+ />
683
+ );
684
+ };
685
+
686
+ export const ImplementationLoader = (props) => {
687
+ const { search } = useLocation();
688
+ const { business_concept_id, id } = queryString.parse(search);
689
+ return id && business_concept_id ? (
690
+ <ImplementationConceptLoader
691
+ {...props}
692
+ conceptId={business_concept_id}
693
+ conceptVersionId={id}
694
+ />
695
+ ) : (
696
+ <ImplementationOperatorLoader {...props} />
697
+ );
698
+ };
699
+
636
700
  export default connect(mapStateToProps, {
637
701
  createRuleImplementation,
638
702
  updateRuleImplementation,
@@ -1,16 +1,14 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
- import { useIntl } from "react-intl";
5
4
  import { connect } from "react-redux";
6
- import { Header, Label, Grid, Icon } from "semantic-ui-react";
5
+ import { Header, Grid, Icon } from "semantic-ui-react";
7
6
  import ImplementationActions from "./ImplementationActions";
8
7
  import ImplementationResultBar from "./ImplementationResultBar";
9
8
  import RuleImplementationTabs from "./RuleImplementationTabs";
10
9
  import Subscription from "./Subscription";
11
10
 
12
11
  export const RuleImplementation = ({ children, ruleImplementation }) => {
13
- const { formatMessage } = useIntl();
14
12
  const deletedAt = _.prop("deleted_at")(ruleImplementation);
15
13
 
16
14
  return (
@@ -27,6 +27,8 @@ export const InformationForm = ({
27
27
  ruleImplementation,
28
28
  setIsValid,
29
29
  onDomainsLoad,
30
+ conceptDomainId,
31
+ domainIds,
30
32
  }) => {
31
33
  const { formatMessage } = useIntl();
32
34
 
@@ -35,7 +37,7 @@ export const InformationForm = ({
35
37
 
36
38
  setIsValid(_.isEmpty(valid));
37
39
  };
38
- const domainId = ruleImplementation?.domain_id;
40
+ const domainId = conceptDomainId || ruleImplementation?.domain_id;
39
41
  const domainActions = ["publishImplementation", "manageSegments"];
40
42
 
41
43
  return (
@@ -82,12 +84,13 @@ export const InformationForm = ({
82
84
  </label>
83
85
  <DomainSelector
84
86
  action={
85
- ruleImplementation.rule_id
86
- ? "manageImplementations"
87
+ conceptDomainId
88
+ ? "manageLinkedImplementations"
87
89
  : "manageRulelessImplementations"
88
90
  }
89
- value={ruleImplementation.domain_id}
91
+ value={domainId}
90
92
  domainActions={domainActions}
93
+ domainIds={domainIds}
91
94
  onLoad={(data) => onDomainsLoad(data.domains)}
92
95
  onChange={(_e, { value }) => onChange("domain_id", value)}
93
96
  labels
@@ -120,6 +123,8 @@ InformationForm.propTypes = {
120
123
  setImplementationKey: PropTypes.func,
121
124
  setIsValid: PropTypes.func,
122
125
  onDomainsLoad: PropTypes.func,
126
+ conceptDomainId: PropTypes.array,
127
+ domainIds: PropTypes.array,
123
128
  };
124
129
 
125
130
  export default InformationForm;
@@ -41,12 +41,15 @@ RuleImplementationStep.propTypes = {
41
41
  activeStep: PropTypes.string,
42
42
  name: PropTypes.string,
43
43
  icon: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
44
+ disabled: PropTypes.bool,
44
45
  };
45
46
 
46
47
  export const RuleImplementationForm = ({
47
48
  addPopulation,
48
49
  addSegment,
49
50
  addValidation,
51
+ conceptDomainId,
52
+ domainIds,
50
53
  isSubmitting,
51
54
  onChange,
52
55
  onSubmit,
@@ -342,6 +345,8 @@ export const RuleImplementationForm = ({
342
345
  onChange,
343
346
  ruleImplementation,
344
347
  setImplementationKey,
348
+ conceptDomainId,
349
+ domainIds,
345
350
  setIsValid: setContentIsValid,
346
351
  onDomainsLoad: (domains) => {
347
352
  setDomains(domains);
@@ -505,6 +510,8 @@ RuleImplementationForm.propTypes = {
505
510
  addValidation: PropTypes.func,
506
511
  addSegment: PropTypes.func,
507
512
  authManageSegments: PropTypes.bool,
513
+ conceptDomainId: PropTypes.number,
514
+ domainIds: PropTypes.array,
508
515
  isAdmin: PropTypes.bool,
509
516
  isSubmitting: PropTypes.bool,
510
517
  onChange: PropTypes.func,
@@ -519,9 +526,7 @@ RuleImplementationForm.propTypes = {
519
526
  };
520
527
 
521
528
  const mapStateToProps = ({ ruleImplementationCreating }) => {
522
- return {
523
- isSubmitting: ruleImplementationCreating,
524
- };
529
+ return { isSubmitting: ruleImplementationCreating };
525
530
  };
526
531
 
527
532
  export default connect(mapStateToProps)(RuleImplementationForm);
@@ -2,7 +2,7 @@ import _ from "lodash/fp";
2
2
  import React, { useState, useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { connect } from "react-redux";
5
- import { useHistory } from "react-router-dom";
5
+ import { useHistory, useLocation } from "react-router-dom";
6
6
  import { FormattedMessage, useIntl } from "react-intl";
7
7
  import { gql, useQuery } from "@apollo/client";
8
8
  import { Loading } from "@truedat/core/components";
@@ -26,6 +26,12 @@ const DomainActionsLoader = ({ id, actions, onLoad }) => {
26
26
  return null;
27
27
  };
28
28
 
29
+ DomainActionsLoader.propTypes = {
30
+ id: PropTypes.number,
31
+ actions: PropTypes.array,
32
+ onLoad: PropTypes.func,
33
+ };
34
+
29
35
  const Help = ({ message }) => {
30
36
  const { formatMessage } = useIntl();
31
37
  return (
@@ -45,6 +51,8 @@ Help.propTypes = {
45
51
  };
46
52
 
47
53
  export const RuleImplementationRawForm = ({
54
+ conceptDomainId,
55
+ domainIds,
48
56
  implementationKey,
49
57
  isSubmitting,
50
58
  onChange,
@@ -57,9 +65,11 @@ export const RuleImplementationRawForm = ({
57
65
  sources,
58
66
  sourcesLoading,
59
67
  }) => {
60
- const domainActions = ["publishImplementation"];
61
68
  const { formatMessage } = useIntl();
62
69
  const history = useHistory();
70
+
71
+ const domainActions = ["publishImplementation"];
72
+
63
73
  const [isContentValid, setIsContentValid] = useState();
64
74
 
65
75
  const [domains, setDomains] = useState();
@@ -172,7 +182,8 @@ export const RuleImplementationRawForm = ({
172
182
 
173
183
  const doCancel = () => history.goBack();
174
184
  const errors = getErrors();
175
- const domainId = ruleImplementation?.domain_id || rule?.domain_id;
185
+ const domainId =
186
+ conceptDomainId || ruleImplementation?.domain_id || rule?.domain_id;
176
187
  return (
177
188
  <Form className="rule">
178
189
  <Form.Radio
@@ -202,12 +213,13 @@ export const RuleImplementationRawForm = ({
202
213
  <Form.Field>
203
214
  <DomainSelector
204
215
  action={
205
- ruleImplementation.rule_id
206
- ? "manageRawImplementations"
216
+ conceptDomainId
217
+ ? "manageLinkedRawImplementations"
207
218
  : "manageRawRulelessImplementations"
208
219
  }
209
- value={ruleImplementation.domain_id}
220
+ value={domainId}
210
221
  domainActions={domainActions}
222
+ domainIds={domainIds}
211
223
  onLoad={(data) => setDomains(data.domains)}
212
224
  onChange={(_e, { value }) => onChange("domain_id", value)}
213
225
  labels
@@ -371,6 +383,8 @@ export const RuleImplementationRawForm = ({
371
383
  };
372
384
 
373
385
  RuleImplementationRawForm.propTypes = {
386
+ conceptDomainId: PropTypes.number,
387
+ domainIds: PropTypes.array,
374
388
  implementationKey: PropTypes.string,
375
389
  isSubmitting: PropTypes.bool,
376
390
  onChange: PropTypes.func,
@@ -0,0 +1 @@
1
+ export * from "./useConcept";
@@ -0,0 +1,13 @@
1
+ import { compile } from "path-to-regexp";
2
+ import useSWR from "swr";
3
+ import { API_BUSINESS_CONCEPT_VERSION } from "@truedat/bg/concepts/api";
4
+ import { apiJson } from "@truedat/core/services/api";
5
+
6
+ const toApiPath = compile(API_BUSINESS_CONCEPT_VERSION);
7
+
8
+ export const useConcept = (business_concept_id, id) => {
9
+ const url = toApiPath({ business_concept_id, id });
10
+ const { data, error } = useSWR(url, apiJson);
11
+ const concept = data?.data;
12
+ return { concept, error, loading: !error && !data };
13
+ };
@@ -3,6 +3,7 @@ export default {
3
3
  "alert.createExecutionGroup.success.content":
4
4
  "{count} implementations queued for execution",
5
5
  "alert.createExecutionGroup.success.header": "Executions scheduled",
6
+ "alert.createImplementationConcept.failed.header": "Error linking to concept",
6
7
  "alert.createImplementationStructure.failed.header":
7
8
  "Error linking to structure",
8
9
  "alert.createRule.failed.header": "Error creating rule",
@@ -15,6 +16,7 @@ export default {
15
16
  "alert.updateRuleImplementation.failed.header":
16
17
  "Error updating implementation",
17
18
  "concepts.rules.empty": "This concept has no quality rules",
19
+ "concepts.implementations.empty": "This concept has no implementations",
18
20
  "createImplementationStructure.error.implementation_id.has already been taken":
19
21
  "A link with same structure and type already exists",
20
22
  "createRule.error.domain_id.required": "Domain is required",
@@ -3,6 +3,7 @@ export default {
3
3
  "alert.createExecutionGroup.success.content":
4
4
  "Se ha solicitado la ejecución de {count} implementaciones",
5
5
  "alert.createExecutionGroup.success.header": "Ejecuciones programadas",
6
+ "alert.createImplementationConcept.failed.header": "Error al vincular con el concepto",
6
7
  "alert.createImplementationStructure.failed.header":
7
8
  "Error al vincular con estructura",
8
9
  "alert.createRule.failed.header": "Error creando regla",
@@ -15,6 +16,7 @@ export default {
15
16
  "alert.updateRuleImplementation.failed.header":
16
17
  "Error actualizando implementación",
17
18
  "concepts.rules.empty": "Ese concepto no tiene reglas de calidad",
19
+ "concepts.implementations.empty": "Ese concepto no tiene implementaciones",
18
20
  "createImplementationStructure.error.implementation_id.has already been taken":
19
21
  "Una relación con misma estructura y tipo ya existe",
20
22
  "createRule.error.domain_id.required": "Dominio es requerido",
@@ -1,6 +1,7 @@
1
1
  import _ from "lodash/fp";
2
2
  import { dismissAlert } from "@truedat/core/routines";
3
3
  import {
4
+ createRuleImplementation,
4
5
  uploadRules,
5
6
  uploadImplementations,
6
7
  uploadResults,
@@ -14,6 +15,17 @@ const dqMessage = (state = initialState, { type, payload }) => {
14
15
  switch (type) {
15
16
  case dismissAlert.TRIGGER:
16
17
  return initialState;
18
+ case createRuleImplementation.FAILURE:
19
+ if (payload.status != 500 && payload.message) {
20
+ return {
21
+ error: true,
22
+ header: "alert.createImplementationConcept.failed.header",
23
+ icon: "attention",
24
+ text: "",
25
+ };
26
+ } else {
27
+ return null;
28
+ }
17
29
  case uploadRules.SUCCESS:
18
30
  const messages = _.has("errors")(payload.data)
19
31
  ? _.flow(
@@ -26,10 +26,19 @@ export const ruleImplementationRedirect = (
26
26
  return initialState;
27
27
  case createRuleImplementation.SUCCESS: {
28
28
  const id = payload?.data?.id;
29
- return id
29
+ return payload?.redirectUrl
30
+ ? payload.redirectUrl
31
+ : id
30
32
  ? linkTo.IMPLEMENTATION({ implementation_id: id })
31
33
  : linkTo.IMPLEMENTATIONS();
32
34
  }
35
+ case createRuleImplementation.FAILURE: {
36
+ return payload?.implementation_id
37
+ ? linkTo.IMPLEMENTATION_CONCEPT_LINKS({
38
+ implementation_id: payload?.implementation_id,
39
+ })
40
+ : state;
41
+ }
33
42
  case createImplementationStructure.SUCCESS: {
34
43
  return payload?.redirectUrl;
35
44
  }
@@ -1,5 +1,6 @@
1
1
  import { testSaga } from "redux-saga-test-plan";
2
2
  import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
3
+ import { API_RELATIONS } from "@truedat/lm/api";
3
4
  import {
4
5
  createRuleImplementationRequestSaga,
5
6
  createRuleImplementationSaga,
@@ -53,8 +54,20 @@ describe("sagas: createRuleImplementationSaga", () => {
53
54
  ],
54
55
  rule_id: 1,
55
56
  };
57
+
58
+ const relation = {
59
+ context: {},
60
+ source_id: 2,
61
+ source_type: "implementation_ref",
62
+ target_id: 1,
63
+ target_type: "business_concept",
64
+ tag_ids: [],
65
+ };
66
+
56
67
  const data = { rule_implementation };
68
+ const relationData = { relation };
57
69
  const url = API_RULE_IMPLEMENTATIONS;
70
+ const relationsUrl = API_RELATIONS;
58
71
  const payload = { rule_implementation };
59
72
 
60
73
  it("should encode request content and put a success action when a response is returned", () => {
@@ -67,7 +80,7 @@ describe("sagas: createRuleImplementationSaga", () => {
67
80
  .next(rule_implementation)
68
81
  .call(apiJsonPost, url, data, JSON_OPTS)
69
82
  .next({ data })
70
- .put(createRuleImplementation.success(data))
83
+ .put(createRuleImplementation.success({ data, redirectUrl: undefined }))
71
84
  .next()
72
85
  .put(clearStructure.trigger())
73
86
  .next()
@@ -97,4 +110,29 @@ describe("sagas: createRuleImplementationSaga", () => {
97
110
  .isDone();
98
111
  }).not.toThrow();
99
112
  });
113
+
114
+ it("should encode request and call relation if have conceptId and redirectUrl", () => {
115
+ const redirectUrl = "/concepts/1/versions/2/links/implementations";
116
+ const payload = { rule_implementation, conceptId: 1, redirectUrl };
117
+
118
+ expect(() => {
119
+ testSaga(createRuleImplementationSaga, { payload })
120
+ .next()
121
+ .put(createRuleImplementation.request())
122
+ .next()
123
+ .call(encodeRawContent, rule_implementation)
124
+ .next(rule_implementation)
125
+ .call(apiJsonPost, url, data, JSON_OPTS)
126
+ .next({ data })
127
+ // .call(apiJsonPost, relationsUrl, relationData, JSON_OPTS)
128
+ .next()
129
+ .put(createRuleImplementation.success({ data, redirectUrl }))
130
+ .next()
131
+ .put(clearStructure.trigger())
132
+ .next()
133
+ .put(createRuleImplementation.fulfill())
134
+ .next()
135
+ .isDone();
136
+ }).not.toThrow();
137
+ });
100
138
  });
@@ -1,23 +1,51 @@
1
+ import _ from "lodash/fp";
1
2
  import { call, put, takeLatest } from "redux-saga/effects";
2
3
  import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
4
+ import { API_RELATIONS } from "@truedat/lm/api";
3
5
  import { createRuleImplementation, clearStructure } from "../routines";
4
6
  import { API_RULE_IMPLEMENTATIONS } from "../api";
5
7
  import { encodeRawContent } from "../services/encodeRawContent";
6
8
 
7
9
  export function* createRuleImplementationSaga({ payload }) {
8
10
  try {
9
- const { rule_implementation } = payload;
11
+ const { rule_implementation, redirectUrl, conceptId } = payload;
10
12
  const url = API_RULE_IMPLEMENTATIONS;
11
13
  yield put(createRuleImplementation.request());
12
14
  const encoded = yield call(encodeRawContent, rule_implementation);
13
15
  const requestData = { rule_implementation: encoded };
14
16
  const { data } = yield call(apiJsonPost, url, requestData, JSON_OPTS);
15
- yield put(createRuleImplementation.success(data));
17
+ if (conceptId && redirectUrl) {
18
+ const relationsUrl = API_RELATIONS;
19
+ const relation = {
20
+ context: {},
21
+ source_id: data?.data?.implementation_ref,
22
+ source_type: "implementation_ref",
23
+ target_id: conceptId,
24
+ target_type: "business_concept",
25
+ tag_ids: [],
26
+ };
27
+ const relationData = { relation };
28
+ yield call(apiJsonPost, relationsUrl, relationData, JSON_OPTS);
29
+ }
30
+ yield put(createRuleImplementation.success({ data, redirectUrl }));
16
31
  yield put(clearStructure.trigger());
17
32
  } catch (error) {
18
33
  if (error.response) {
19
34
  const { status, data } = error.response;
20
- yield put(createRuleImplementation.failure({ status, data }));
35
+ const responseURL = error.response?.request?.responseURL;
36
+ if (_.includes("/api/relations")(responseURL)) {
37
+ const { relation } = JSON.parse(error.config.data);
38
+ yield put(
39
+ createRuleImplementation.failure({
40
+ status,
41
+ data,
42
+ message: "ruleImplementation.linkConcept.failed",
43
+ implementation_id: relation.source_id,
44
+ })
45
+ );
46
+ } else {
47
+ yield put(createRuleImplementation.failure({ status, data }));
48
+ }
21
49
  } else {
22
50
  yield put(createRuleImplementation.failure(error.message));
23
51
  }