@atlaskit/link-datasource 1.12.4 → 1.13.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/analytics/constants.js +1 -1
  3. package/dist/cjs/ui/jira-issues-modal/basic-filters/utils/isClauseTooComplex.js +93 -0
  4. package/dist/cjs/ui/jira-issues-modal/basic-filters/utils/isQueryTooComplex.js +146 -0
  5. package/dist/cjs/ui/jira-issues-modal/jira-search-container/buildJQL.js +3 -2
  6. package/dist/cjs/ui/jira-issues-modal/jira-search-container/index.js +21 -8
  7. package/dist/cjs/ui/jira-issues-modal/jira-search-container/messages.js +5 -0
  8. package/dist/cjs/ui/jira-issues-modal/mode-switcher/index.js +28 -16
  9. package/dist/es2019/analytics/constants.js +1 -1
  10. package/dist/es2019/ui/jira-issues-modal/basic-filters/utils/isClauseTooComplex.js +77 -0
  11. package/dist/es2019/ui/jira-issues-modal/basic-filters/utils/isQueryTooComplex.js +105 -0
  12. package/dist/es2019/ui/jira-issues-modal/jira-search-container/buildJQL.js +2 -1
  13. package/dist/es2019/ui/jira-issues-modal/jira-search-container/index.js +13 -3
  14. package/dist/es2019/ui/jira-issues-modal/jira-search-container/messages.js +5 -0
  15. package/dist/es2019/ui/jira-issues-modal/mode-switcher/index.js +19 -9
  16. package/dist/esm/analytics/constants.js +1 -1
  17. package/dist/esm/ui/jira-issues-modal/basic-filters/utils/isClauseTooComplex.js +86 -0
  18. package/dist/esm/ui/jira-issues-modal/basic-filters/utils/isQueryTooComplex.js +140 -0
  19. package/dist/esm/ui/jira-issues-modal/jira-search-container/buildJQL.js +2 -1
  20. package/dist/esm/ui/jira-issues-modal/jira-search-container/index.js +21 -8
  21. package/dist/esm/ui/jira-issues-modal/jira-search-container/messages.js +5 -0
  22. package/dist/esm/ui/jira-issues-modal/mode-switcher/index.js +29 -17
  23. package/dist/types/ui/jira-issues-modal/basic-filters/utils/isClauseTooComplex.d.ts +2 -0
  24. package/dist/types/ui/jira-issues-modal/basic-filters/utils/isQueryTooComplex.d.ts +1 -0
  25. package/dist/types/ui/jira-issues-modal/jira-search-container/buildJQL.d.ts +1 -0
  26. package/dist/types/ui/jira-issues-modal/jira-search-container/index.d.ts +1 -0
  27. package/dist/types/ui/jira-issues-modal/jira-search-container/messages.d.ts +5 -0
  28. package/dist/types/ui/jira-issues-modal/mode-switcher/index.d.ts +2 -0
  29. package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/utils/isClauseTooComplex.d.ts +2 -0
  30. package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/utils/isQueryTooComplex.d.ts +1 -0
  31. package/dist/types-ts4.5/ui/jira-issues-modal/jira-search-container/buildJQL.d.ts +1 -0
  32. package/dist/types-ts4.5/ui/jira-issues-modal/jira-search-container/index.d.ts +1 -0
  33. package/dist/types-ts4.5/ui/jira-issues-modal/jira-search-container/messages.d.ts +5 -0
  34. package/dist/types-ts4.5/ui/jira-issues-modal/mode-switcher/index.d.ts +2 -0
  35. package/package.json +1 -2
  36. package/dist/cjs/ui/jira-issues-modal/basic-filters/hooks/useIsComplexQuery.js +0 -12
  37. package/dist/es2019/ui/jira-issues-modal/basic-filters/hooks/useIsComplexQuery.js +0 -6
  38. package/dist/esm/ui/jira-issues-modal/basic-filters/hooks/useIsComplexQuery.js +0 -6
  39. package/dist/types/ui/jira-issues-modal/basic-filters/hooks/useIsComplexQuery.d.ts +0 -3
  40. package/dist/types-ts4.5/ui/jira-issues-modal/basic-filters/hooks/useIsComplexQuery.d.ts +0 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/link-datasource
2
2
 
3
+ ## 1.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#42523](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/42523) [`8cf0628d658`](https://bitbucket.org/atlassian/atlassian-frontend/commits/8cf0628d658) - Add logic to disable switching to Basic mode when the query is complex
8
+
3
9
  ## 1.12.4
4
10
 
5
11
  ### Patch Changes
@@ -7,5 +7,5 @@ exports.packageMetaData = exports.EVENT_CHANNEL = void 0;
7
7
  var EVENT_CHANNEL = exports.EVENT_CHANNEL = 'media';
8
8
  var packageMetaData = exports.packageMetaData = {
9
9
  packageName: "@atlaskit/link-datasource",
10
- packageVersion: "1.12.4"
10
+ packageVersion: "1.13.0"
11
11
  };
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.isClauseTooComplex = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _jqlAst = require("@atlaskit/jql-ast");
10
+ var _buildJQL = require("../../jira-search-container/buildJQL");
11
+ var removeFuzzyCharacter = function removeFuzzyCharacter(value) {
12
+ if (value !== null && value !== void 0 && value.endsWith(_buildJQL.fuzzyCharacter)) {
13
+ return value.slice(0, -1);
14
+ }
15
+ return value;
16
+ };
17
+ var getValueFromTerminalClause = function getValueFromTerminalClause(clause) {
18
+ var operand = clause.operand;
19
+ return operand !== undefined && operand.operandType === _jqlAst.OPERAND_TYPE_VALUE && removeFuzzyCharacter(operand.value) || undefined;
20
+ };
21
+ var areClauseFieldValuesEqual = function areClauseFieldValuesEqual(clauseA, clauseB, clauseC) {
22
+ var valueA = clauseA && getValueFromTerminalClause(clauseA);
23
+ var valueB = clauseB && getValueFromTerminalClause(clauseB);
24
+ var valueC = clauseC && getValueFromTerminalClause(clauseC);
25
+ var values = [valueA, valueB, valueC].filter(Boolean);
26
+
27
+ // checks if valid fields, text, summary and key have the same value, if not, its a complex query and cannnot be recreated in basic mode
28
+ return values.length > 1 && values.every(function (value) {
29
+ return value === values[0];
30
+ });
31
+ };
32
+ var areClauseFieldKeysAllowed = function areClauseFieldKeysAllowed(clauseA, clauseB, clauseC) {
33
+ var fieldA = clauseA.field.value;
34
+ var fieldB = clauseB.field.value;
35
+ var fieldC = clauseC === null || clauseC === void 0 ? void 0 : clauseC.field.value; // clauseC only if jql with 3 OR clauses, 'text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC',
36
+
37
+ return [fieldA, fieldB, fieldC].filter(Boolean).every(function (field) {
38
+ return ['summary', 'text', 'key'].includes(field);
39
+ });
40
+ };
41
+ var doesCompoundClauseContainAllTerminalClauses = function doesCompoundClauseContainAllTerminalClauses(clauses) {
42
+ return clauses.every(function (clauses) {
43
+ return clauses.clauseType === _jqlAst.CLAUSE_TYPE_TERMINAL;
44
+ });
45
+ };
46
+ var isClauseTooComplex = exports.isClauseTooComplex = function isClauseTooComplex(clauses, key) {
47
+ if (key === 'text') {
48
+ var _clauses = (0, _slicedToArray2.default)(clauses, 1),
49
+ clause = _clauses[0];
50
+ if (clause.clauseType === _jqlAst.CLAUSE_TYPE_COMPOUND) {
51
+ var textClauses = clause.clauses;
52
+
53
+ /**
54
+ * valid: text ~ "test*" or summary ~ "test*" ORDER BY created DESC
55
+ * valid: text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC
56
+ * invalid: assignee = "me" or text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC
57
+ */
58
+ if (textClauses.length !== 2 && textClauses.length !== 3) {
59
+ return true;
60
+ }
61
+
62
+ /**
63
+ * valid: text ~ "test*" or summary ~ "test*"
64
+ * invalid: text ~ "test" or (summary ~ "test" or key = "test")
65
+ */
66
+ if (!doesCompoundClauseContainAllTerminalClauses(textClauses)) {
67
+ return true;
68
+ }
69
+ var _textClauses = (0, _slicedToArray2.default)(textClauses, 3),
70
+ clauseA = _textClauses[0],
71
+ clauseB = _textClauses[1],
72
+ clauseC = _textClauses[2];
73
+
74
+ /**
75
+ * valid: text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC
76
+ * invalid: text ~ "EDM-6023*" or summary ~ "anotherValue" ORDER BY created DESC
77
+ * invalid: text ~ "EDM-6023*" or text ~ "anotherValue" ORDER BY created DESC
78
+ */
79
+ if (!areClauseFieldValuesEqual(clauseA, clauseB, clauseC)) {
80
+ return true;
81
+ }
82
+
83
+ /**
84
+ * valid: text ~ "EDM-6023*" ORDER BY created DESC
85
+ * invalid: resolution = 40134 ORDER BY created DESC
86
+ */
87
+ if (!areClauseFieldKeysAllowed(clauseA, clauseB, clauseC)) {
88
+ return true;
89
+ }
90
+ }
91
+ }
92
+ return clauses.length > 1;
93
+ };
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.isQueryTooComplex = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
12
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
13
+ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
14
+ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
15
+ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
16
+ var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
17
+ var _mergeWith = _interopRequireDefault(require("lodash/mergeWith"));
18
+ var _jqlAst = require("@atlaskit/jql-ast");
19
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
20
+ var _jiraSearchContainer = require("../../jira-search-container");
21
+ var _isClauseTooComplex = require("./isClauseTooComplex");
22
+ var _index = require("./index");
23
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
24
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
25
+ // Map of field keys to their respective clauses in the Jast
26
+
27
+ var allowedFields = [
28
+ // basic filter fields
29
+ 'assignee', 'issuetype', 'project', 'status',
30
+ // search input fields
31
+ 'text', 'summary', 'key',
32
+ // orderby field
33
+ 'created'];
34
+ var fallbackOperators = [_jqlAst.OPERATOR_IN];
35
+ var fieldSpecificOperators = {
36
+ text: [_jqlAst.OPERATOR_LIKE, _jqlAst.OPERATOR_EQUALS],
37
+ summary: [_jqlAst.OPERATOR_LIKE, _jqlAst.OPERATOR_EQUALS],
38
+ key: [_jqlAst.OPERATOR_EQUALS],
39
+ created: [_jqlAst.OPERATOR_GT_EQUALS],
40
+ project: [_jqlAst.OPERATOR_IN, _jqlAst.OPERATOR_EQUALS],
41
+ issuetype: [_jqlAst.OPERATOR_IN, _jqlAst.OPERATOR_EQUALS],
42
+ status: [_jqlAst.OPERATOR_IN, _jqlAst.OPERATOR_EQUALS],
43
+ assignee: [_jqlAst.OPERATOR_IN, _jqlAst.OPERATOR_EQUALS]
44
+ };
45
+ var JqlClauseCollectingVisitorError = /*#__PURE__*/function (_Error) {
46
+ (0, _inherits2.default)(JqlClauseCollectingVisitorError, _Error);
47
+ var _super = _createSuper(JqlClauseCollectingVisitorError);
48
+ function JqlClauseCollectingVisitorError() {
49
+ (0, _classCallCheck2.default)(this, JqlClauseCollectingVisitorError);
50
+ return _super.apply(this, arguments);
51
+ }
52
+ return (0, _createClass2.default)(JqlClauseCollectingVisitorError);
53
+ }( /*#__PURE__*/(0, _wrapNativeSuper2.default)(Error));
54
+ /**
55
+ * Rather than having to navigate the entire tree structure ourself, we extend AbstractJastVisitor
56
+ * class and implement visitor functions for node types that we wish to process.
57
+ * A list of available visitor can be viewed in packages/jql/jql-ast/src/types/api/jast-visitor.ts
58
+ * more info - https://atlaskit.atlassian.com/packages/jql/jql-ast/docs/traversing-the-ast
59
+ * */
60
+ var JqlClauseCollectingVisitor = /*#__PURE__*/function (_AbstractJastVisitor) {
61
+ (0, _inherits2.default)(JqlClauseCollectingVisitor, _AbstractJastVisitor);
62
+ var _super2 = _createSuper(JqlClauseCollectingVisitor);
63
+ function JqlClauseCollectingVisitor() {
64
+ var _this;
65
+ (0, _classCallCheck2.default)(this, JqlClauseCollectingVisitor);
66
+ _this = _super2.call(this);
67
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "visitNotClause", function () {
68
+ throw new JqlClauseCollectingVisitorError('Visited an unsupported node while traversing the AST');
69
+ });
70
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "visitOrderByField", function (orderByField) {
71
+ var _orderByField$field$v;
72
+ var fieldValue = (_orderByField$field$v = orderByField.field.value) === null || _orderByField$field$v === void 0 ? void 0 : _orderByField$field$v.toLowerCase();
73
+ if (fieldValue && !_jiraSearchContainer.ALLOWED_ORDER_BY_KEYS.includes(fieldValue)) {
74
+ throw new JqlClauseCollectingVisitorError("query with order by field '".concat(fieldValue, "' is not supported"));
75
+ }
76
+ return {};
77
+ });
78
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "visitCompoundClause", function (compoundClause) {
79
+ var clauseMap = {};
80
+ var operator = compoundClause.operator.value;
81
+ if (operator === _jqlAst.COMPOUND_OPERATOR_AND) {
82
+ return compoundClause.clauses.reduce(function (result, clause) {
83
+ return _this.aggregateResult(clause.accept((0, _assertThisInitialized2.default)(_this)), result);
84
+ }, clauseMap);
85
+ }
86
+ if (operator === _jqlAst.COMPOUND_OPERATOR_OR) {
87
+ // this is delt with in isClauseTooComplex
88
+ return _this.aggregateResult({
89
+ text: [compoundClause]
90
+ }, clauseMap);
91
+ }
92
+ throw new JqlClauseCollectingVisitorError("Compound clauses using the operator '".concat(operator, "' is not supported"));
93
+ });
94
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "visitTerminalClause", function (terminalClause) {
95
+ var _terminalClause$opera;
96
+ var fieldName = terminalClause.field.value.toLowerCase();
97
+ if (!allowedFields.includes(fieldName)) {
98
+ throw new JqlClauseCollectingVisitorError("Field with name '".concat(fieldName, "' of type ").concat(terminalClause.clauseType, " is not supported"));
99
+ }
100
+ var operator = (_terminalClause$opera = terminalClause.operator) === null || _terminalClause$opera === void 0 ? void 0 : _terminalClause$opera.value;
101
+ var allowedOperators = fieldSpecificOperators[fieldName] || fallbackOperators;
102
+ if (operator && !allowedOperators.includes(operator.toLowerCase())) {
103
+ throw new JqlClauseCollectingVisitorError("Field with name '".concat(fieldName, "' using operator ").concat(operator, " is not supported"));
104
+ }
105
+ return (0, _defineProperty2.default)({}, terminalClause.field.value.toLowerCase(), [terminalClause]);
106
+ });
107
+ return _this;
108
+ }
109
+ (0, _createClass2.default)(JqlClauseCollectingVisitor, [{
110
+ key: "aggregateResult",
111
+ value: function aggregateResult(aggregate, nextResult) {
112
+ return (0, _mergeWith.default)(aggregate, nextResult, function (destValue, srcValue) {
113
+ return srcValue.concat(destValue !== null && destValue !== void 0 ? destValue : []);
114
+ });
115
+ }
116
+ }, {
117
+ key: "defaultResult",
118
+ value: function defaultResult() {
119
+ return {};
120
+ }
121
+ }]);
122
+ return JqlClauseCollectingVisitor;
123
+ }(_jqlAst.AbstractJastVisitor);
124
+ var isQueryTooComplex = exports.isQueryTooComplex = function isQueryTooComplex(jql) {
125
+ if (!(0, _platformFeatureFlags.getBooleanFF)('platform.linking-platform.datasource.show-jlol-basic-filters') || !jql) {
126
+ return false;
127
+ }
128
+ if (!(0, _index.isValidJql)(jql)) {
129
+ return true;
130
+ }
131
+ var jast = new _jqlAst.JastBuilder().build(jql);
132
+ try {
133
+ var jqlClauseCollectingVisitor = new JqlClauseCollectingVisitor();
134
+ var clauseMap = jast.query ? jast.query.accept(jqlClauseCollectingVisitor) : {}; // jast.query is defined as void | Query, hence the fallback
135
+
136
+ var hasAnyKeyWithComplexClause = Object.entries(clauseMap).some(function (_ref2) {
137
+ var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
138
+ key = _ref3[0],
139
+ clauses = _ref3[1];
140
+ return (0, _isClauseTooComplex.isClauseTooComplex)(clauses, key);
141
+ });
142
+ return hasAnyKeyWithComplexClause;
143
+ } catch (error) {
144
+ return true;
145
+ }
146
+ };
@@ -4,11 +4,12 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.buildJQL = void 0;
7
+ exports.fuzzyCharacter = exports.buildJQL = void 0;
8
8
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
9
  var _jqlAst = require("@atlaskit/jql-ast");
10
10
  var fuzzySearchRegExp = /^"(.+)"$/;
11
11
  var jiraIssueKeyRegExp = /[A-Z]+-\d+/;
12
+ var fuzzyCharacter = exports.fuzzyCharacter = '*';
12
13
  var constructTerminalClause = function constructTerminalClause(field, operator, value) {
13
14
  return _jqlAst.creators.terminalClause(_jqlAst.creators.field(field), _jqlAst.creators.operator(operator), _jqlAst.creators.valueOperand(value));
14
15
  };
@@ -30,7 +31,7 @@ var buildJQL = exports.buildJQL = function buildJQL(input) {
30
31
  return '';
31
32
  }
32
33
  if (trimmedRawSearch) {
33
- var fuzzy = !trimmedRawSearch.match(fuzzySearchRegExp) ? '*' : '';
34
+ var fuzzy = !trimmedRawSearch.match(fuzzySearchRegExp) ? fuzzyCharacter : '';
34
35
  var basicSearch = trimmedRawSearch.replace(/['"?*]+/g, '');
35
36
  var text = constructTerminalClause('text', _jqlAst.OPERATOR_LIKE, "".concat(basicSearch).concat(fuzzy));
36
37
  var summary = constructTerminalClause('summary', _jqlAst.OPERATOR_LIKE, "".concat(basicSearch).concat(fuzzy));
@@ -5,7 +5,7 @@ var _typeof = require("@babel/runtime/helpers/typeof");
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.JiraSearchContainer = void 0;
8
+ exports.JiraSearchContainer = exports.ALLOWED_ORDER_BY_KEYS = void 0;
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _react = _interopRequireWildcard(require("react"));
11
11
  var _react2 = require("@emotion/react");
@@ -13,6 +13,7 @@ var _reactIntlNext = require("react-intl-next");
13
13
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
14
  var _analytics = require("../../../analytics");
15
15
  var _basicFilters = require("../basic-filters");
16
+ var _isQueryTooComplex = require("../basic-filters/utils/isQueryTooComplex");
16
17
  var _basicSearchInput = require("../basic-search-input");
17
18
  var _jqlEditor = require("../jql-editor");
18
19
  var _modeSwitcher = require("../mode-switcher");
@@ -29,6 +30,7 @@ var inputContainerStyles = (0, _react2.css)({
29
30
  minHeight: '60px'
30
31
  });
31
32
  var DEFAULT_JQL_QUERY = 'created >= -30d order by created DESC';
33
+ var ALLOWED_ORDER_BY_KEYS = exports.ALLOWED_ORDER_BY_KEYS = ['key', 'summary', 'assignee', 'status', 'created'];
32
34
  var JiraSearchMethodSwitcher = _modeSwitcher.ModeSwitcher;
33
35
  var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchContainer(props) {
34
36
  var isSearching = props.isSearching,
@@ -53,14 +55,18 @@ var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchConta
53
55
  _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
54
56
  jql = _useState6[0],
55
57
  setJql = _useState6[1];
56
- var _useState7 = (0, _react.useState)(),
58
+ var _useState7 = (0, _react.useState)(false),
57
59
  _useState8 = (0, _slicedToArray2.default)(_useState7, 2),
58
- orderKey = _useState8[0],
59
- setOrderKey = _useState8[1];
60
+ isComplexQuery = _useState8[0],
61
+ setIsComplexQuery = _useState8[1];
60
62
  var _useState9 = (0, _react.useState)(),
61
63
  _useState10 = (0, _slicedToArray2.default)(_useState9, 2),
62
- orderDirection = _useState10[0],
63
- setOrderDirection = _useState10[1];
64
+ orderKey = _useState10[0],
65
+ setOrderKey = _useState10[1];
66
+ var _useState11 = (0, _react.useState)(),
67
+ _useState12 = (0, _slicedToArray2.default)(_useState11, 2),
68
+ orderDirection = _useState12[0],
69
+ setOrderDirection = _useState12[1];
64
70
  var _useDatasourceAnalyti = (0, _analytics.useDatasourceAnalyticsEvents)(),
65
71
  fireEvent = _useDatasourceAnalyti.fireEvent;
66
72
  var onSearchMethodChange = (0, _react.useCallback)(function (searchMethod) {
@@ -87,7 +93,7 @@ var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchConta
87
93
  var order = hasOrder ? (_fragments$at3 = fragments.at(-1)) === null || _fragments$at3 === void 0 ? void 0 : _fragments$at3.split(' ').at(-1) : undefined;
88
94
 
89
95
  // TODO: confirm if these are the only order keys we want to preserve - existing whiteboard logic
90
- if (key && ['key', 'summary', 'assignee', 'status'].includes(key)) {
96
+ if (key && ALLOWED_ORDER_BY_KEYS.includes(key)) {
91
97
  setOrderKey(key);
92
98
  setOrderDirection(order);
93
99
  }
@@ -97,6 +103,7 @@ var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchConta
97
103
  onSearch({
98
104
  jql: jql
99
105
  }, currentSearchMethod);
106
+ setIsComplexQuery((0, _isQueryTooComplex.isQueryTooComplex)(jql));
100
107
  if (currentSearchMethod === 'basic') {
101
108
  fireEvent('ui.form.submitted.basicSearch', {});
102
109
  } else if (currentSearchMethod === 'jql') {
@@ -109,6 +116,10 @@ var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchConta
109
116
  }
110
117
  return false;
111
118
  }, []);
119
+ (0, _react.useEffect)(function () {
120
+ setIsComplexQuery((0, _isQueryTooComplex.isQueryTooComplex)(jql));
121
+ // eslint-disable-next-line react-hooks/exhaustive-deps
122
+ }, []);
112
123
  return (0, _react2.jsx)("div", {
113
124
  css: inputContainerStyles
114
125
  }, currentSearchMethod === 'basic' && (0, _react2.jsx)(_react.default.Fragment, null, (0, _react2.jsx)(_basicSearchInput.BasicSearchInput, {
@@ -133,7 +144,9 @@ var JiraSearchContainer = exports.JiraSearchContainer = function JiraSearchConta
133
144
  value: 'jql'
134
145
  }, {
135
146
  label: formatMessage(_messages.modeSwitcherMessages.basicTextSearchLabel),
136
- value: 'basic'
147
+ value: 'basic',
148
+ disabled: isComplexQuery,
149
+ tooltipText: isComplexQuery ? formatMessage(_messages.modeSwitcherMessages.basicModeSwitchDisabledTooltipText) : ''
137
150
  }]
138
151
  }));
139
152
  };
@@ -10,5 +10,10 @@ var modeSwitcherMessages = exports.modeSwitcherMessages = (0, _reactIntlNext.def
10
10
  id: 'linkDataSource.jira-issues.configmodal.basicModeText',
11
11
  description: 'Display text for basic text search toggle button',
12
12
  defaultMessage: 'Basic'
13
+ },
14
+ basicModeSwitchDisabledTooltipText: {
15
+ id: 'linkDataSource.jira-issues.configmodal.basicModeSwitchDisabledTooltipText',
16
+ description: 'Display tooltip text when basic mode switch is disabled',
17
+ defaultMessage: "You can't switch to basic for this query."
13
18
  }
14
19
  });
@@ -5,9 +5,11 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.ModeSwitcher = void 0;
8
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
8
9
  var _react = _interopRequireDefault(require("react"));
9
10
  var _react2 = require("@emotion/react");
10
11
  var _colors = require("@atlaskit/theme/colors");
12
+ var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
11
13
  /** @jsx jsx */
12
14
 
13
15
  var modeSwitcherStyles = (0, _react2.css)({
@@ -52,8 +54,12 @@ var modeSwitcherLabelSelectedStyles = (0, _react2.css)({
52
54
  }
53
55
  });
54
56
  var modeSwitcherLabelDisabledStyles = (0, _react2.css)({
57
+ color: "var(--ds-text-disabled, ".concat(_colors.N60, ")")
58
+ });
59
+ var modeSwitcherDisabledStyles = (0, _react2.css)({
55
60
  ':hover': {
56
- cursor: 'not-allowed'
61
+ cursor: 'not-allowed',
62
+ background: 'transparent'
57
63
  }
58
64
  });
59
65
  var compactModeSwitcherLabelStyles = (0, _react2.css)({
@@ -76,21 +82,27 @@ var ModeSwitcher = exports.ModeSwitcher = function ModeSwitcher(props) {
76
82
  disabled: isDisabled
77
83
  }, options.map(function (_ref) {
78
84
  var value = _ref.value,
79
- label = _ref.label;
85
+ label = _ref.label,
86
+ isOptionDisabled = _ref.disabled,
87
+ tooltipText = _ref.tooltipText;
80
88
  var isSelected = value === selectedOptionValue;
81
- return (0, _react2.jsx)("label", {
82
- key: value,
83
- css: [modeSwitcherLabelStyles, isCompact && compactModeSwitcherLabelStyles, isSelected && modeSwitcherLabelSelectedStyles, isDisabled && modeSwitcherLabelDisabledStyles],
84
- "data-testid": "mode-toggle-".concat(value)
85
- }, label, (0, _react2.jsx)("input", {
86
- "aria-checked": isSelected,
87
- "aria-disabled": isDisabled,
88
- checked: isSelected,
89
- css: modeInputStyles,
90
- disabled: isDisabled,
91
- onChange: handleModeChange,
92
- type: "radio",
93
- value: value
94
- }));
89
+ return (0, _react2.jsx)(_tooltip.default, {
90
+ content: tooltipText
91
+ }, function (tooltipProps) {
92
+ return (0, _react2.jsx)("label", (0, _extends2.default)({}, tooltipProps, {
93
+ key: value,
94
+ css: [modeSwitcherLabelStyles, isCompact && compactModeSwitcherLabelStyles, isSelected && modeSwitcherLabelSelectedStyles, isDisabled && modeSwitcherDisabledStyles, isOptionDisabled && [modeSwitcherLabelDisabledStyles, modeSwitcherDisabledStyles]],
95
+ "data-testid": "mode-toggle-".concat(value)
96
+ }), label, (0, _react2.jsx)("input", {
97
+ "aria-checked": isSelected,
98
+ "aria-disabled": isOptionDisabled,
99
+ checked: isSelected,
100
+ css: modeInputStyles,
101
+ disabled: isOptionDisabled,
102
+ onChange: handleModeChange,
103
+ type: "radio",
104
+ value: value
105
+ }));
106
+ });
95
107
  })) : null;
96
108
  };
@@ -1,5 +1,5 @@
1
1
  export const EVENT_CHANNEL = 'media';
2
2
  export const packageMetaData = {
3
3
  packageName: "@atlaskit/link-datasource",
4
- packageVersion: "1.12.4"
4
+ packageVersion: "1.13.0"
5
5
  };
@@ -0,0 +1,77 @@
1
+ import { CLAUSE_TYPE_COMPOUND, CLAUSE_TYPE_TERMINAL, OPERAND_TYPE_VALUE } from '@atlaskit/jql-ast';
2
+ import { fuzzyCharacter } from '../../jira-search-container/buildJQL';
3
+ const removeFuzzyCharacter = value => {
4
+ if (value !== null && value !== void 0 && value.endsWith(fuzzyCharacter)) {
5
+ return value.slice(0, -1);
6
+ }
7
+ return value;
8
+ };
9
+ const getValueFromTerminalClause = clause => {
10
+ const {
11
+ operand
12
+ } = clause;
13
+ return operand !== undefined && operand.operandType === OPERAND_TYPE_VALUE && removeFuzzyCharacter(operand.value) || undefined;
14
+ };
15
+ const areClauseFieldValuesEqual = (clauseA, clauseB, clauseC) => {
16
+ const valueA = clauseA && getValueFromTerminalClause(clauseA);
17
+ const valueB = clauseB && getValueFromTerminalClause(clauseB);
18
+ const valueC = clauseC && getValueFromTerminalClause(clauseC);
19
+ const values = [valueA, valueB, valueC].filter(Boolean);
20
+
21
+ // checks if valid fields, text, summary and key have the same value, if not, its a complex query and cannnot be recreated in basic mode
22
+ return values.length > 1 && values.every(value => value === values[0]);
23
+ };
24
+ const areClauseFieldKeysAllowed = (clauseA, clauseB, clauseC) => {
25
+ const fieldA = clauseA.field.value;
26
+ const fieldB = clauseB.field.value;
27
+ const fieldC = clauseC === null || clauseC === void 0 ? void 0 : clauseC.field.value; // clauseC only if jql with 3 OR clauses, 'text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC',
28
+
29
+ return [fieldA, fieldB, fieldC].filter(Boolean).every(field => ['summary', 'text', 'key'].includes(field));
30
+ };
31
+ const doesCompoundClauseContainAllTerminalClauses = clauses => {
32
+ return clauses.every(clauses => clauses.clauseType === CLAUSE_TYPE_TERMINAL);
33
+ };
34
+ export const isClauseTooComplex = (clauses, key) => {
35
+ if (key === 'text') {
36
+ const [clause] = clauses;
37
+ if (clause.clauseType === CLAUSE_TYPE_COMPOUND) {
38
+ const textClauses = clause.clauses;
39
+
40
+ /**
41
+ * valid: text ~ "test*" or summary ~ "test*" ORDER BY created DESC
42
+ * valid: text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC
43
+ * invalid: assignee = "me" or text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC
44
+ */
45
+ if (textClauses.length !== 2 && textClauses.length !== 3) {
46
+ return true;
47
+ }
48
+
49
+ /**
50
+ * valid: text ~ "test*" or summary ~ "test*"
51
+ * invalid: text ~ "test" or (summary ~ "test" or key = "test")
52
+ */
53
+ if (!doesCompoundClauseContainAllTerminalClauses(textClauses)) {
54
+ return true;
55
+ }
56
+ const [clauseA, clauseB, clauseC] = textClauses;
57
+
58
+ /**
59
+ * valid: text ~ "EDM-6023*" or summary ~ "EDM-6023*" or key = EDM-6023 ORDER BY created DESC
60
+ * invalid: text ~ "EDM-6023*" or summary ~ "anotherValue" ORDER BY created DESC
61
+ * invalid: text ~ "EDM-6023*" or text ~ "anotherValue" ORDER BY created DESC
62
+ */
63
+ if (!areClauseFieldValuesEqual(clauseA, clauseB, clauseC)) {
64
+ return true;
65
+ }
66
+
67
+ /**
68
+ * valid: text ~ "EDM-6023*" ORDER BY created DESC
69
+ * invalid: resolution = 40134 ORDER BY created DESC
70
+ */
71
+ if (!areClauseFieldKeysAllowed(clauseA, clauseB, clauseC)) {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+ return clauses.length > 1;
77
+ };
@@ -0,0 +1,105 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import mergeWith from 'lodash/mergeWith';
3
+ import { AbstractJastVisitor, COMPOUND_OPERATOR_AND, COMPOUND_OPERATOR_OR, JastBuilder, OPERATOR_EQUALS, OPERATOR_GT_EQUALS, OPERATOR_IN, OPERATOR_LIKE } from '@atlaskit/jql-ast';
4
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
5
+ import { ALLOWED_ORDER_BY_KEYS } from '../../jira-search-container';
6
+ import { isClauseTooComplex } from './isClauseTooComplex';
7
+ import { isValidJql } from './index';
8
+
9
+ // Map of field keys to their respective clauses in the Jast
10
+
11
+ const allowedFields = [
12
+ // basic filter fields
13
+ 'assignee', 'issuetype', 'project', 'status',
14
+ // search input fields
15
+ 'text', 'summary', 'key',
16
+ // orderby field
17
+ 'created'];
18
+ const fallbackOperators = [OPERATOR_IN];
19
+ const fieldSpecificOperators = {
20
+ text: [OPERATOR_LIKE, OPERATOR_EQUALS],
21
+ summary: [OPERATOR_LIKE, OPERATOR_EQUALS],
22
+ key: [OPERATOR_EQUALS],
23
+ created: [OPERATOR_GT_EQUALS],
24
+ project: [OPERATOR_IN, OPERATOR_EQUALS],
25
+ issuetype: [OPERATOR_IN, OPERATOR_EQUALS],
26
+ status: [OPERATOR_IN, OPERATOR_EQUALS],
27
+ assignee: [OPERATOR_IN, OPERATOR_EQUALS]
28
+ };
29
+ class JqlClauseCollectingVisitorError extends Error {}
30
+
31
+ /**
32
+ * Rather than having to navigate the entire tree structure ourself, we extend AbstractJastVisitor
33
+ * class and implement visitor functions for node types that we wish to process.
34
+ * A list of available visitor can be viewed in packages/jql/jql-ast/src/types/api/jast-visitor.ts
35
+ * more info - https://atlaskit.atlassian.com/packages/jql/jql-ast/docs/traversing-the-ast
36
+ * */
37
+ class JqlClauseCollectingVisitor extends AbstractJastVisitor {
38
+ constructor() {
39
+ super();
40
+ _defineProperty(this, "visitNotClause", () => {
41
+ throw new JqlClauseCollectingVisitorError('Visited an unsupported node while traversing the AST');
42
+ });
43
+ _defineProperty(this, "visitOrderByField", orderByField => {
44
+ var _orderByField$field$v;
45
+ const fieldValue = (_orderByField$field$v = orderByField.field.value) === null || _orderByField$field$v === void 0 ? void 0 : _orderByField$field$v.toLowerCase();
46
+ if (fieldValue && !ALLOWED_ORDER_BY_KEYS.includes(fieldValue)) {
47
+ throw new JqlClauseCollectingVisitorError(`query with order by field '${fieldValue}' is not supported`);
48
+ }
49
+ return {};
50
+ });
51
+ _defineProperty(this, "visitCompoundClause", compoundClause => {
52
+ const clauseMap = {};
53
+ const operator = compoundClause.operator.value;
54
+ if (operator === COMPOUND_OPERATOR_AND) {
55
+ return compoundClause.clauses.reduce((result, clause) => this.aggregateResult(clause.accept(this), result), clauseMap);
56
+ }
57
+ if (operator === COMPOUND_OPERATOR_OR) {
58
+ // this is delt with in isClauseTooComplex
59
+ return this.aggregateResult({
60
+ text: [compoundClause]
61
+ }, clauseMap);
62
+ }
63
+ throw new JqlClauseCollectingVisitorError(`Compound clauses using the operator '${operator}' is not supported`);
64
+ });
65
+ _defineProperty(this, "visitTerminalClause", terminalClause => {
66
+ var _terminalClause$opera;
67
+ const fieldName = terminalClause.field.value.toLowerCase();
68
+ if (!allowedFields.includes(fieldName)) {
69
+ throw new JqlClauseCollectingVisitorError(`Field with name '${fieldName}' of type ${terminalClause.clauseType} is not supported`);
70
+ }
71
+ const operator = (_terminalClause$opera = terminalClause.operator) === null || _terminalClause$opera === void 0 ? void 0 : _terminalClause$opera.value;
72
+ const allowedOperators = fieldSpecificOperators[fieldName] || fallbackOperators;
73
+ if (operator && !allowedOperators.includes(operator.toLowerCase())) {
74
+ throw new JqlClauseCollectingVisitorError(`Field with name '${fieldName}' using operator ${operator} is not supported`);
75
+ }
76
+ return {
77
+ [terminalClause.field.value.toLowerCase()]: [terminalClause]
78
+ };
79
+ });
80
+ }
81
+ aggregateResult(aggregate, nextResult) {
82
+ return mergeWith(aggregate, nextResult, (destValue, srcValue) => srcValue.concat(destValue !== null && destValue !== void 0 ? destValue : []));
83
+ }
84
+ defaultResult() {
85
+ return {};
86
+ }
87
+ }
88
+ export const isQueryTooComplex = jql => {
89
+ if (!getBooleanFF('platform.linking-platform.datasource.show-jlol-basic-filters') || !jql) {
90
+ return false;
91
+ }
92
+ if (!isValidJql(jql)) {
93
+ return true;
94
+ }
95
+ const jast = new JastBuilder().build(jql);
96
+ try {
97
+ const jqlClauseCollectingVisitor = new JqlClauseCollectingVisitor();
98
+ const clauseMap = jast.query ? jast.query.accept(jqlClauseCollectingVisitor) : {}; // jast.query is defined as void | Query, hence the fallback
99
+
100
+ const hasAnyKeyWithComplexClause = Object.entries(clauseMap).some(([key, clauses]) => isClauseTooComplex(clauses, key));
101
+ return hasAnyKeyWithComplexClause;
102
+ } catch (error) {
103
+ return true;
104
+ }
105
+ };
@@ -1,6 +1,7 @@
1
1
  import { COMPOUND_OPERATOR_AND, COMPOUND_OPERATOR_OR, creators, JastBuilder, OPERATOR_EQUALS, OPERATOR_GT_EQUALS, OPERATOR_IN, OPERATOR_LIKE, ORDER_BY_DIRECTION_ASC, ORDER_BY_DIRECTION_DESC, print } from '@atlaskit/jql-ast';
2
2
  const fuzzySearchRegExp = /^"(.+)"$/;
3
3
  const jiraIssueKeyRegExp = /[A-Z]+-\d+/;
4
+ export const fuzzyCharacter = '*';
4
5
  const constructTerminalClause = (field, operator, value) => creators.terminalClause(creators.field(field), creators.operator(operator), creators.valueOperand(value));
5
6
  export const buildJQL = input => {
6
7
  /**
@@ -22,7 +23,7 @@ export const buildJQL = input => {
22
23
  return '';
23
24
  }
24
25
  if (trimmedRawSearch) {
25
- const fuzzy = !trimmedRawSearch.match(fuzzySearchRegExp) ? '*' : '';
26
+ const fuzzy = !trimmedRawSearch.match(fuzzySearchRegExp) ? fuzzyCharacter : '';
26
27
  const basicSearch = trimmedRawSearch.replace(/['"?*]+/g, '');
27
28
  const text = constructTerminalClause('text', OPERATOR_LIKE, `${basicSearch}${fuzzy}`);
28
29
  const summary = constructTerminalClause('summary', OPERATOR_LIKE, `${basicSearch}${fuzzy}`);