@atlaskit/link-datasource 1.19.22 → 1.19.23

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 (24) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/analytics/constants.js +1 -1
  3. package/dist/cjs/hooks/useValidateAqlText.js +83 -45
  4. package/dist/cjs/ui/assets-modal/modal/index.js +20 -34
  5. package/dist/cjs/ui/assets-modal/modal/render-assets-content/index.js +18 -15
  6. package/dist/cjs/ui/assets-modal/search-container/aql-search-input/index.js +14 -84
  7. package/dist/cjs/ui/assets-modal/search-container/index.js +1 -1
  8. package/dist/es2019/analytics/constants.js +1 -1
  9. package/dist/es2019/hooks/useValidateAqlText.js +70 -31
  10. package/dist/es2019/ui/assets-modal/modal/index.js +5 -4
  11. package/dist/es2019/ui/assets-modal/modal/render-assets-content/index.js +19 -16
  12. package/dist/es2019/ui/assets-modal/search-container/aql-search-input/index.js +13 -56
  13. package/dist/es2019/ui/assets-modal/search-container/index.js +1 -1
  14. package/dist/esm/analytics/constants.js +1 -1
  15. package/dist/esm/hooks/useValidateAqlText.js +83 -45
  16. package/dist/esm/ui/assets-modal/modal/index.js +20 -34
  17. package/dist/esm/ui/assets-modal/modal/render-assets-content/index.js +19 -16
  18. package/dist/esm/ui/assets-modal/search-container/aql-search-input/index.js +14 -85
  19. package/dist/esm/ui/assets-modal/search-container/index.js +1 -1
  20. package/dist/types/hooks/useValidateAqlText.d.ts +21 -5
  21. package/dist/types/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
  22. package/dist/types-ts4.5/hooks/useValidateAqlText.d.ts +21 -5
  23. package/dist/types-ts4.5/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
  24. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @atlaskit/link-datasource
2
2
 
3
+ ## 1.19.23
4
+
5
+ ### Patch Changes
6
+
7
+ - [#57785](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/57785) [`846c9ef4fb27`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/846c9ef4fb27) - Update AQL validation hook for Assets Config Modal
8
+ - Updated dependencies
9
+
3
10
  ## 1.19.22
4
11
 
5
12
  ### 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.19.22"
10
+ packageVersion: "1.19.23"
11
11
  };
@@ -4,81 +4,119 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.useValidateAqlText = void 0;
7
+ exports.useValidateAqlText = exports.SEARCH_DEBOUNCE = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
9
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
10
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
11
  var _react = require("react");
12
12
  var _analytics = require("../analytics");
13
13
  var _cmdbService = require("../services/cmdbService");
14
- var useValidateAqlText = exports.useValidateAqlText = function useValidateAqlText(workspaceId) {
15
- var _useState = (0, _react.useState)(false),
14
+ var SEARCH_DEBOUNCE = exports.SEARCH_DEBOUNCE = 350;
15
+ var useValidateAqlText = exports.useValidateAqlText = function useValidateAqlText(workspaceId, initialValue) {
16
+ var timeout = (0, _react.useRef)();
17
+ var lastValue = (0, _react.useRef)('');
18
+ var lastResult = (0, _react.useRef)(Promise.resolve(undefined));
19
+ var _useState = (0, _react.useState)(initialValue.trim() === '' ? {
20
+ type: 'idle'
21
+ } : {
22
+ type: 'loading'
23
+ }),
16
24
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
17
- loading = _useState2[0],
18
- setLoading = _useState2[1];
19
- var _useState3 = (0, _react.useState)(false),
20
- _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
21
- isValidAqlText = _useState4[0],
22
- setIsValidAqlText = _useState4[1];
23
- var _useState5 = (0, _react.useState)(),
24
- _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
25
- error = _useState6[0],
26
- setError = _useState6[1];
25
+ lastValidationResult = _useState2[0],
26
+ setLastValidationResult = _useState2[1];
27
27
  var _useDatasourceAnalyti = (0, _analytics.useDatasourceAnalyticsEvents)(),
28
28
  fireEvent = _useDatasourceAnalyti.fireEvent;
29
+
30
+ // We return undefined when valid and 'error' when invalid
29
31
  var validateAqlText = (0, _react.useCallback)( /*#__PURE__*/function () {
30
32
  var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(aql) {
31
- var isValid, message, _validateAqlResponse$, validateAqlResponse;
33
+ var validateAqlResponse, _validateAqlResponse$, _validateAqlResponse$2;
32
34
  return _regenerator.default.wrap(function _callee$(_context) {
33
35
  while (1) switch (_context.prev = _context.next) {
34
36
  case 0:
35
- setLoading(true);
36
- setError(undefined);
37
- isValid = false;
38
- message = null;
39
- _context.prev = 4;
40
- _context.next = 7;
37
+ if (!(aql !== null && aql !== void 0 && aql.trim())) {
38
+ _context.next = 18;
39
+ break;
40
+ }
41
+ _context.prev = 1;
42
+ _context.next = 4;
41
43
  return (0, _cmdbService.validateAql)(workspaceId, {
42
44
  qlQuery: aql
43
45
  }, fireEvent);
44
- case 7:
46
+ case 4:
45
47
  validateAqlResponse = _context.sent;
46
- setIsValidAqlText(validateAqlResponse.isValid);
47
- isValid = validateAqlResponse.isValid;
48
- message = ((_validateAqlResponse$ = validateAqlResponse.errors) === null || _validateAqlResponse$ === void 0 ? void 0 : _validateAqlResponse$.iql) || null;
49
- _context.next = 16;
50
- break;
51
- case 13:
52
- _context.prev = 13;
53
- _context.t0 = _context["catch"](4);
54
- if (_context.t0 instanceof Error) {
55
- setError(_context.t0);
56
- } else {
57
- setError(new Error('Unexpected error occured'));
48
+ if (!validateAqlResponse.isValid) {
49
+ _context.next = 10;
50
+ break;
58
51
  }
59
- case 16:
60
- _context.prev = 16;
61
- setLoading(false);
62
- return _context.finish(16);
63
- case 19:
64
- return _context.abrupt("return", {
65
- isValid: isValid,
66
- message: message
52
+ setLastValidationResult({
53
+ type: 'valid',
54
+ validatedAql: aql
67
55
  });
56
+ return _context.abrupt("return", undefined);
57
+ case 10:
58
+ setLastValidationResult({
59
+ type: 'invalid',
60
+ error: (_validateAqlResponse$ = (_validateAqlResponse$2 = validateAqlResponse.errors) === null || _validateAqlResponse$2 === void 0 ? void 0 : _validateAqlResponse$2.iql) !== null && _validateAqlResponse$ !== void 0 ? _validateAqlResponse$ : ''
61
+ });
62
+ return _context.abrupt("return", 'error');
63
+ case 12:
64
+ _context.next = 18;
65
+ break;
66
+ case 14:
67
+ _context.prev = 14;
68
+ _context.t0 = _context["catch"](1);
69
+ setLastValidationResult({
70
+ type: 'invalid',
71
+ error: ''
72
+ });
73
+ return _context.abrupt("return", 'error');
74
+ case 18:
75
+ setLastValidationResult({
76
+ type: 'idle'
77
+ });
78
+ return _context.abrupt("return", undefined);
68
79
  case 20:
69
80
  case "end":
70
81
  return _context.stop();
71
82
  }
72
- }, _callee, null, [[4, 13, 16, 19]]);
83
+ }, _callee, null, [[1, 14]]);
73
84
  }));
74
85
  return function (_x) {
75
86
  return _ref.apply(this, arguments);
76
87
  };
77
88
  }(), [workspaceId, fireEvent]);
89
+
90
+ /* Debounce async validation for input, validation is also called on every field change
91
+ in a form so we need to also memoize. The async validate function is expected to either:
92
+ Immediately return a promise (which is then collected into an array, every single time validation is run),
93
+ or immediately return either undefined or an error message */
94
+ var debouncedValidation = function debouncedValidation(value) {
95
+ return new Promise(function (resolve) {
96
+ if (timeout.current) {
97
+ timeout.current();
98
+ }
99
+ if (value !== lastValue.current) {
100
+ var timerId = setTimeout(function () {
101
+ setLastValidationResult({
102
+ type: 'loading'
103
+ });
104
+ lastValue.current = value;
105
+ lastResult.current = validateAqlText(value);
106
+ resolve(lastResult.current);
107
+ }, SEARCH_DEBOUNCE);
108
+ timeout.current = function () {
109
+ clearTimeout(timerId);
110
+ resolve('debouncing');
111
+ };
112
+ } else {
113
+ resolve(lastResult.current);
114
+ }
115
+ });
116
+ };
78
117
  return {
79
- isValidAqlText: isValidAqlText,
118
+ debouncedValidation: debouncedValidation,
80
119
  validateAqlText: validateAqlText,
81
- validateAqlTextLoading: loading,
82
- validateAqlTextError: error
120
+ lastValidationResult: lastValidationResult
83
121
  };
84
122
  };
@@ -6,9 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
8
  exports.AssetsConfigModal = void 0;
9
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
10
9
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
11
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
14
12
  var _react = require("react");
@@ -286,36 +284,24 @@ var PlainAssetsConfigModal = function PlainAssetsConfigModal(props) {
286
284
  }).fire(_analytics.EVENT_CHANNEL);
287
285
  onCancel();
288
286
  }, [analyticsPayload, onCancel]);
289
- var handleOnSearch = (0, _react.useCallback)( /*#__PURE__*/function () {
290
- var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(searchAql, searchSchemaId) {
291
- return _regenerator.default.wrap(function _callee$(_context) {
292
- while (1) switch (_context.prev = _context.next) {
293
- case 0:
294
- if (schemaId !== searchSchemaId || aql !== searchAql || status === 'rejected') {
295
- searchCount.current++;
296
- if (schemaId !== searchSchemaId) {
297
- userInteractionActions.current.add(_types.DatasourceAction.SCHEMA_UPDATED);
298
- }
299
- if (aql !== searchAql) {
300
- userInteractionActions.current.add(_types.DatasourceAction.QUERY_UPDATED);
301
- }
302
- reset({
303
- shouldResetColumns: true
304
- });
305
- setAql(searchAql);
306
- setSchemaId(searchSchemaId);
307
- setIsNewSearch(true);
308
- }
309
- case 1:
310
- case "end":
311
- return _context.stop();
312
- }
313
- }, _callee);
314
- }));
315
- return function (_x, _x2) {
316
- return _ref.apply(this, arguments);
317
- };
318
- }(), [aql, reset, schemaId, status]);
287
+ var handleOnSearch = (0, _react.useCallback)(function (searchAql, searchSchemaId) {
288
+ if (schemaId !== searchSchemaId || aql !== searchAql || status === 'rejected') {
289
+ searchCount.current++;
290
+ if (schemaId !== searchSchemaId) {
291
+ userInteractionActions.current.add(_types.DatasourceAction.SCHEMA_UPDATED);
292
+ }
293
+ if (aql !== searchAql) {
294
+ userInteractionActions.current.add(_types.DatasourceAction.QUERY_UPDATED);
295
+ }
296
+ reset({
297
+ shouldResetColumns: true,
298
+ shouldForceRequest: true
299
+ });
300
+ setAql(searchAql);
301
+ setSchemaId(searchSchemaId);
302
+ setIsNewSearch(true);
303
+ }
304
+ }, [aql, reset, schemaId, status]);
319
305
  var renderErrorState = (0, _react.useCallback)(function () {
320
306
  if (errorState) {
321
307
  switch (errorState) {
@@ -340,7 +326,7 @@ var PlainAssetsConfigModal = function PlainAssetsConfigModal(props) {
340
326
  return (0, _react2.jsx)(_searchContainer.AssetsSearchContainer, {
341
327
  workspaceId: workspaceId,
342
328
  initialSearchData: {
343
- aql: aql,
329
+ aql: initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.aql,
344
330
  objectSchema: existingObjectSchema,
345
331
  objectSchemas: objectSchemas
346
332
  },
@@ -349,7 +335,7 @@ var PlainAssetsConfigModal = function PlainAssetsConfigModal(props) {
349
335
  isSearching: status === 'loading'
350
336
  });
351
337
  }
352
- }, [errorState, workspaceId, assetsClientLoading, aql, existingObjectSchema, objectSchemas, handleOnSearch, status]);
338
+ }, [errorState, workspaceId, assetsClientLoading, initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.aql, existingObjectSchema, objectSchemas, handleOnSearch, status]);
353
339
  return (0, _react2.jsx)(_intlMessagesProvider.IntlMessagesProvider, {
354
340
  defaultMessages: _en.default,
355
341
  loaderFn: _fetchMessagesForLocale.fetchMessagesForLocale
@@ -91,19 +91,22 @@ var RenderAssetsContent = exports.RenderAssetsContent = function RenderAssetsCon
91
91
  parentContainerRenderInstanceId: modalRenderInstanceId
92
92
  }));
93
93
  }, [columns, defaultVisibleColumnKeys, hasNextPage, loadDatasourceDetails, onNextPage, onVisibleColumnKeysChange, responseItems, status, visibleColumnKeys, modalRenderInstanceId]);
94
- if (isFetchingInitialData) {
95
- // Placing this check first as it's a priority before all others
96
- return (0, _react2.jsx)(LoadingView, null);
97
- } else if (status === 'rejected') {
98
- return (0, _react2.jsx)(RejectedView, null);
99
- } else if (status === 'unauthorized') {
100
- return (0, _react2.jsx)(UnauthorizedView, null);
101
- } else if (status === 'empty') {
102
- return (0, _react2.jsx)(EmptyView, null);
103
- } else if (resolvedWithNoResults) {
104
- return (0, _react2.jsx)(NoResultsView, null);
105
- } else if (status === 'loading' && !columns.length) {
106
- return (0, _react2.jsx)(LoadingView, null);
107
- }
108
- return issueLikeDataTableView;
94
+ var renderAssetsContentView = (0, _react.useCallback)(function () {
95
+ if (isFetchingInitialData) {
96
+ // Placing this check first as it's a priority before all others
97
+ return (0, _react2.jsx)(LoadingView, null);
98
+ } else if (status === 'rejected') {
99
+ return (0, _react2.jsx)(RejectedView, null);
100
+ } else if (status === 'unauthorized') {
101
+ return (0, _react2.jsx)(UnauthorizedView, null);
102
+ } else if (status === 'empty') {
103
+ return (0, _react2.jsx)(EmptyView, null);
104
+ } else if (resolvedWithNoResults) {
105
+ return (0, _react2.jsx)(NoResultsView, null);
106
+ } else if (status === 'loading' && !columns.length) {
107
+ return (0, _react2.jsx)(LoadingView, null);
108
+ }
109
+ return issueLikeDataTableView;
110
+ }, [columns.length, isFetchingInitialData, issueLikeDataTableView, resolvedWithNoResults, status]);
111
+ return renderAssetsContentView();
109
112
  };
@@ -4,11 +4,8 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.SEARCH_DEBOUNCE_MS = exports.AqlSearchInput = void 0;
8
- var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
7
+ exports.AqlSearchInput = void 0;
9
8
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
10
- var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
12
9
  var _react = require("react");
13
10
  var _react2 = require("@emotion/react");
14
11
  var _reactIntlNext = require("react-intl-next");
@@ -28,9 +25,6 @@ var _styled = require("../styled");
28
25
  var _messages = require("./messages");
29
26
  /** @jsx jsx */
30
27
 
31
- /* Meta isn't exported in @atlaskit/form
32
- Taken from packages/design-system/form/src/field.tsx */
33
-
34
28
  var buttonBaseStyles = (0, _react2.css)({
35
29
  display: 'flex',
36
30
  height: '100%',
@@ -44,15 +38,14 @@ var AQLSupportDocumentLink = 'https://support.atlassian.com/jira-service-managem
44
38
  var searchButtonStyles = (0, _react2.css)({
45
39
  marginRight: "var(--ds-space-075, 6px)"
46
40
  });
47
- var SEARCH_DEBOUNCE_MS = exports.SEARCH_DEBOUNCE_MS = 350;
48
- var renderValidatorIcon = function renderValidatorIcon(value, error, meta) {
49
- if (value && meta !== null && meta !== void 0 && meta.validating) {
41
+ var renderValidatorIcon = function renderValidatorIcon(lastValidationResult) {
42
+ if (lastValidationResult.type === 'loading') {
50
43
  return (0, _react2.jsx)(_spinner.default, {
51
44
  size: "medium",
52
45
  testId: "assets-datasource-modal--aql-validating"
53
46
  });
54
47
  }
55
- if (value && error) {
48
+ if (lastValidationResult.type === 'invalid') {
56
49
  return (0, _react2.jsx)(_crossCircle.default, {
57
50
  label: "label",
58
51
  primaryColor: "red",
@@ -60,7 +53,7 @@ var renderValidatorIcon = function renderValidatorIcon(value, error, meta) {
60
53
  testId: "assets-datasource-modal--aql-invalid"
61
54
  });
62
55
  }
63
- if (value && meta.valid) {
56
+ if (lastValidationResult.type === 'valid') {
64
57
  return (0, _react2.jsx)(_checkCircle.default, {
65
58
  label: "label",
66
59
  primaryColor: "green",
@@ -82,85 +75,22 @@ var AqlSearchInput = exports.AqlSearchInput = function AqlSearchInput(_ref) {
82
75
  isSearching = _ref.isSearching;
83
76
  var _useIntl = (0, _reactIntlNext.useIntl)(),
84
77
  formatMessage = _useIntl.formatMessage;
85
- var timeout = (0, _react.useRef)();
86
- var lastValue = (0, _react.useRef)(value);
87
- var lastResult = (0, _react.useRef)(Promise.resolve(undefined));
88
- var _useState = (0, _react.useState)(null),
89
- _useState2 = (0, _slicedToArray2.default)(_useState, 2),
90
- message = _useState2[0],
91
- setMessage = _useState2[1];
92
- var _useValidateAqlText = (0, _useValidateAqlText2.useValidateAqlText)(workspaceId),
93
- validateAqlText = _useValidateAqlText.validateAqlText;
94
-
95
- // Validation expects undefined when valid and a string as an error message when invalid
96
- var handleValidation = (0, _react.useCallback)( /*#__PURE__*/function () {
97
- var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(newUnvalidatedQlQuery) {
98
- var validation;
99
- return _regenerator.default.wrap(function _callee$(_context) {
100
- while (1) switch (_context.prev = _context.next) {
101
- case 0:
102
- if (newUnvalidatedQlQuery) {
103
- _context.next = 2;
104
- break;
105
- }
106
- return _context.abrupt("return", undefined);
107
- case 2:
108
- _context.next = 4;
109
- return validateAqlText(newUnvalidatedQlQuery);
110
- case 4:
111
- validation = _context.sent;
112
- setMessage(validation.message);
113
- return _context.abrupt("return", validation.isValid ? undefined : 'invalid');
114
- case 7:
115
- case "end":
116
- return _context.stop();
117
- }
118
- }, _callee);
119
- }));
120
- return function (_x) {
121
- return _ref2.apply(this, arguments);
122
- };
123
- }(), [validateAqlText]);
124
-
125
- /* Debounce async validation for input, validation is also called on every field change
126
- in a form so we need to also memoize. The async validate function is expected to either:
127
- Immediately return a promise (which is then collected into an array, every single time validation is run),
128
- or immediately return either undefined or an error message */
129
- var debouncedMemoizedValidation = function debouncedMemoizedValidation(value) {
130
- return new Promise(function (resolve) {
131
- if (timeout.current) {
132
- timeout.current();
133
- }
134
- if (value !== lastValue.current) {
135
- var timerId = setTimeout(function () {
136
- lastValue.current = value;
137
- lastResult.current = handleValidation(value);
138
- resolve(lastResult.current);
139
- }, SEARCH_DEBOUNCE_MS);
140
- timeout.current = function () {
141
- clearTimeout(timerId);
142
- resolve('debouncing');
143
- };
144
- } else {
145
- resolve(lastResult.current);
146
- }
147
- });
148
- };
78
+ var _useValidateAqlText = (0, _useValidateAqlText2.useValidateAqlText)(workspaceId, value),
79
+ debouncedValidation = _useValidateAqlText.debouncedValidation,
80
+ lastValidationResult = _useValidateAqlText.lastValidationResult;
149
81
  return (0, _react2.jsx)(_styled.FieldContainer, null, (0, _react2.jsx)(_form.Field, {
150
82
  name: _types.aqlKey,
151
83
  defaultValue: value,
152
- validate: debouncedMemoizedValidation
153
- }, function (_ref3) {
154
- var fieldProps = _ref3.fieldProps,
155
- meta = _ref3.meta,
156
- error = _ref3.error;
84
+ validate: debouncedValidation
85
+ }, function (_ref2) {
86
+ var fieldProps = _ref2.fieldProps;
157
87
  return (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)(_textfield.default, (0, _extends2.default)({}, fieldProps, {
158
88
  elemBeforeInput: (0, _react2.jsx)("span", {
159
89
  style: {
160
90
  paddingLeft: 6,
161
91
  width: 24
162
92
  }
163
- }, renderValidatorIcon(fieldProps.value, error, meta)),
93
+ }, renderValidatorIcon(lastValidationResult)),
164
94
  elemAfterInput: (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)(_tooltip.default, {
165
95
  content: formatMessage(_messages.searchInputMessages.helpTooltipText),
166
96
  position: "bottom"
@@ -184,10 +114,10 @@ var AqlSearchInput = exports.AqlSearchInput = function AqlSearchInput(_ref) {
184
114
  spacing: "none",
185
115
  testId: "assets-datasource-modal--aql-search-button",
186
116
  type: "submit",
187
- isDisabled: fieldProps.value.trim() === '' || meta.validating || !meta.valid
117
+ isDisabled: lastValidationResult.type !== 'valid'
188
118
  })),
189
119
  placeholder: formatMessage(_messages.searchInputMessages.placeholder),
190
120
  testId: testId
191
- })), fieldProps.value && error && message && (0, _react2.jsx)(_form.ErrorMessage, null, message));
121
+ })), lastValidationResult.type === 'invalid' && lastValidationResult.error && (0, _react2.jsx)(_form.ErrorMessage, null, lastValidationResult.error));
192
122
  }));
193
123
  };
@@ -27,7 +27,7 @@ var AssetsSearchContainer = exports.AssetsSearchContainer = function AssetsSearc
27
27
  var onFormSubmit = function onFormSubmit(searchFormValues) {
28
28
  var aql = searchFormValues.aql,
29
29
  objectSchema = searchFormValues.objectSchema;
30
- if (objectSchema) {
30
+ if (aql && objectSchema) {
31
31
  fireEvent('ui.aqlEditor.searched', {});
32
32
  // Pass the validated aql and object schema back to modal
33
33
  onSearch(aql, objectSchema.value);
@@ -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.19.22"
4
+ packageVersion: "1.19.23"
5
5
  };
@@ -1,44 +1,83 @@
1
- import { useCallback, useState } from 'react';
1
+ import { useCallback, useRef, useState } from 'react';
2
2
  import { useDatasourceAnalyticsEvents } from '../analytics';
3
3
  import { validateAql } from '../services/cmdbService';
4
- export const useValidateAqlText = workspaceId => {
5
- const [loading, setLoading] = useState(false);
6
- const [isValidAqlText, setIsValidAqlText] = useState(false);
7
- const [error, setError] = useState();
4
+ export const SEARCH_DEBOUNCE = 350;
5
+ export const useValidateAqlText = (workspaceId, initialValue) => {
6
+ const timeout = useRef();
7
+ const lastValue = useRef('');
8
+ const lastResult = useRef(Promise.resolve(undefined));
9
+ const [lastValidationResult, setLastValidationResult] = useState(initialValue.trim() === '' ? {
10
+ type: 'idle'
11
+ } : {
12
+ type: 'loading'
13
+ });
8
14
  const {
9
15
  fireEvent
10
16
  } = useDatasourceAnalyticsEvents();
17
+
18
+ // We return undefined when valid and 'error' when invalid
11
19
  const validateAqlText = useCallback(async aql => {
12
- setLoading(true);
13
- setError(undefined);
14
- let isValid = false;
15
- let message = null;
16
- try {
17
- var _validateAqlResponse$;
18
- const validateAqlResponse = await validateAql(workspaceId, {
19
- qlQuery: aql
20
- }, fireEvent);
21
- setIsValidAqlText(validateAqlResponse.isValid);
22
- isValid = validateAqlResponse.isValid;
23
- message = ((_validateAqlResponse$ = validateAqlResponse.errors) === null || _validateAqlResponse$ === void 0 ? void 0 : _validateAqlResponse$.iql) || null;
24
- } catch (err) {
25
- if (err instanceof Error) {
26
- setError(err);
27
- } else {
28
- setError(new Error('Unexpected error occured'));
20
+ if (aql !== null && aql !== void 0 && aql.trim()) {
21
+ try {
22
+ const validateAqlResponse = await validateAql(workspaceId, {
23
+ qlQuery: aql
24
+ }, fireEvent);
25
+ if (validateAqlResponse.isValid) {
26
+ setLastValidationResult({
27
+ type: 'valid',
28
+ validatedAql: aql
29
+ });
30
+ return undefined;
31
+ } else {
32
+ var _validateAqlResponse$, _validateAqlResponse$2;
33
+ setLastValidationResult({
34
+ type: 'invalid',
35
+ error: (_validateAqlResponse$ = (_validateAqlResponse$2 = validateAqlResponse.errors) === null || _validateAqlResponse$2 === void 0 ? void 0 : _validateAqlResponse$2.iql) !== null && _validateAqlResponse$ !== void 0 ? _validateAqlResponse$ : ''
36
+ });
37
+ return 'error';
38
+ }
39
+ } catch (err) {
40
+ setLastValidationResult({
41
+ type: 'invalid',
42
+ error: ''
43
+ });
44
+ return 'error';
29
45
  }
30
- } finally {
31
- setLoading(false);
32
46
  }
33
- return {
34
- isValid,
35
- message
36
- };
47
+ setLastValidationResult({
48
+ type: 'idle'
49
+ });
50
+ return undefined;
37
51
  }, [workspaceId, fireEvent]);
52
+
53
+ /* Debounce async validation for input, validation is also called on every field change
54
+ in a form so we need to also memoize. The async validate function is expected to either:
55
+ Immediately return a promise (which is then collected into an array, every single time validation is run),
56
+ or immediately return either undefined or an error message */
57
+ const debouncedValidation = value => new Promise(resolve => {
58
+ if (timeout.current) {
59
+ timeout.current();
60
+ }
61
+ if (value !== lastValue.current) {
62
+ const timerId = setTimeout(() => {
63
+ setLastValidationResult({
64
+ type: 'loading'
65
+ });
66
+ lastValue.current = value;
67
+ lastResult.current = validateAqlText(value);
68
+ resolve(lastResult.current);
69
+ }, SEARCH_DEBOUNCE);
70
+ timeout.current = () => {
71
+ clearTimeout(timerId);
72
+ resolve('debouncing');
73
+ };
74
+ } else {
75
+ resolve(lastResult.current);
76
+ }
77
+ });
38
78
  return {
39
- isValidAqlText,
79
+ debouncedValidation,
40
80
  validateAqlText,
41
- validateAqlTextLoading: loading,
42
- validateAqlTextError: error
81
+ lastValidationResult
43
82
  };
44
83
  };
@@ -259,7 +259,7 @@ const PlainAssetsConfigModal = props => {
259
259
  }).fire(EVENT_CHANNEL);
260
260
  onCancel();
261
261
  }, [analyticsPayload, onCancel]);
262
- const handleOnSearch = useCallback(async (searchAql, searchSchemaId) => {
262
+ const handleOnSearch = useCallback((searchAql, searchSchemaId) => {
263
263
  if (schemaId !== searchSchemaId || aql !== searchAql || status === 'rejected') {
264
264
  searchCount.current++;
265
265
  if (schemaId !== searchSchemaId) {
@@ -269,7 +269,8 @@ const PlainAssetsConfigModal = props => {
269
269
  userInteractionActions.current.add(DatasourceAction.QUERY_UPDATED);
270
270
  }
271
271
  reset({
272
- shouldResetColumns: true
272
+ shouldResetColumns: true,
273
+ shouldForceRequest: true
273
274
  });
274
275
  setAql(searchAql);
275
276
  setSchemaId(searchSchemaId);
@@ -300,7 +301,7 @@ const PlainAssetsConfigModal = props => {
300
301
  return jsx(AssetsSearchContainer, {
301
302
  workspaceId: workspaceId,
302
303
  initialSearchData: {
303
- aql,
304
+ aql: initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.aql,
304
305
  objectSchema: existingObjectSchema,
305
306
  objectSchemas
306
307
  },
@@ -309,7 +310,7 @@ const PlainAssetsConfigModal = props => {
309
310
  isSearching: status === 'loading'
310
311
  });
311
312
  }
312
- }, [errorState, workspaceId, assetsClientLoading, aql, existingObjectSchema, objectSchemas, handleOnSearch, status]);
313
+ }, [errorState, workspaceId, assetsClientLoading, initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.aql, existingObjectSchema, objectSchemas, handleOnSearch, status]);
313
314
  return jsx(IntlMessagesProvider, {
314
315
  defaultMessages: i18nEN,
315
316
  loaderFn: fetchMessagesForLocale