@atlaskit/link-datasource 1.19.22 → 1.19.24
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 +13 -0
- package/dist/cjs/analytics/constants.js +1 -1
- package/dist/cjs/hooks/useValidateAqlText.js +83 -45
- package/dist/cjs/ui/assets-modal/modal/index.js +20 -34
- package/dist/cjs/ui/assets-modal/modal/render-assets-content/index.js +18 -15
- package/dist/cjs/ui/assets-modal/search-container/aql-search-input/index.js +14 -84
- package/dist/cjs/ui/assets-modal/search-container/index.js +1 -1
- package/dist/cjs/ui/datasource-table-view/datasourceTableView.js +33 -30
- package/dist/cjs/ui/jira-issues-modal/initial-state-view/messages.js +2 -2
- package/dist/cjs/ui/jira-issues-modal/modal/index.js +8 -2
- package/dist/es2019/analytics/constants.js +1 -1
- package/dist/es2019/hooks/useValidateAqlText.js +70 -31
- package/dist/es2019/ui/assets-modal/modal/index.js +5 -4
- package/dist/es2019/ui/assets-modal/modal/render-assets-content/index.js +19 -16
- package/dist/es2019/ui/assets-modal/search-container/aql-search-input/index.js +13 -56
- package/dist/es2019/ui/assets-modal/search-container/index.js +1 -1
- package/dist/es2019/ui/datasource-table-view/datasourceTableView.js +33 -30
- package/dist/es2019/ui/jira-issues-modal/initial-state-view/messages.js +2 -2
- package/dist/es2019/ui/jira-issues-modal/modal/index.js +8 -2
- package/dist/esm/analytics/constants.js +1 -1
- package/dist/esm/hooks/useValidateAqlText.js +83 -45
- package/dist/esm/ui/assets-modal/modal/index.js +20 -34
- package/dist/esm/ui/assets-modal/modal/render-assets-content/index.js +19 -16
- package/dist/esm/ui/assets-modal/search-container/aql-search-input/index.js +14 -85
- package/dist/esm/ui/assets-modal/search-container/index.js +1 -1
- package/dist/esm/ui/datasource-table-view/datasourceTableView.js +33 -30
- package/dist/esm/ui/jira-issues-modal/initial-state-view/messages.js +2 -2
- package/dist/esm/ui/jira-issues-modal/modal/index.js +8 -2
- package/dist/types/hooks/useValidateAqlText.d.ts +21 -5
- package/dist/types/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
- package/dist/types-ts4.5/hooks/useValidateAqlText.d.ts +21 -5
- package/dist/types-ts4.5/ui/assets-modal/search-container/aql-search-input/index.d.ts +0 -1
- package/package.json +4 -4
|
@@ -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
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
79
|
+
debouncedValidation,
|
|
40
80
|
validateAqlText,
|
|
41
|
-
|
|
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(
|
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @jsx jsx */
|
|
2
|
-
import { useMemo } from 'react';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
3
|
import { css, jsx } from '@emotion/react';
|
|
4
4
|
import { N40 } from '@atlaskit/theme/colors';
|
|
5
5
|
import { AccessRequired } from '../../../common/error-state/access-required';
|
|
@@ -74,19 +74,22 @@ export const RenderAssetsContent = props => {
|
|
|
74
74
|
onVisibleColumnKeysChange: onVisibleColumnKeysChange,
|
|
75
75
|
parentContainerRenderInstanceId: modalRenderInstanceId
|
|
76
76
|
})), [columns, defaultVisibleColumnKeys, hasNextPage, loadDatasourceDetails, onNextPage, onVisibleColumnKeysChange, responseItems, status, visibleColumnKeys, modalRenderInstanceId]);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
const renderAssetsContentView = useCallback(() => {
|
|
78
|
+
if (isFetchingInitialData) {
|
|
79
|
+
// Placing this check first as it's a priority before all others
|
|
80
|
+
return jsx(LoadingView, null);
|
|
81
|
+
} else if (status === 'rejected') {
|
|
82
|
+
return jsx(RejectedView, null);
|
|
83
|
+
} else if (status === 'unauthorized') {
|
|
84
|
+
return jsx(UnauthorizedView, null);
|
|
85
|
+
} else if (status === 'empty') {
|
|
86
|
+
return jsx(EmptyView, null);
|
|
87
|
+
} else if (resolvedWithNoResults) {
|
|
88
|
+
return jsx(NoResultsView, null);
|
|
89
|
+
} else if (status === 'loading' && !columns.length) {
|
|
90
|
+
return jsx(LoadingView, null);
|
|
91
|
+
}
|
|
92
|
+
return issueLikeDataTableView;
|
|
93
|
+
}, [columns.length, isFetchingInitialData, issueLikeDataTableView, resolvedWithNoResults, status]);
|
|
94
|
+
return renderAssetsContentView();
|
|
92
95
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
/** @jsx jsx */
|
|
3
|
-
import { Fragment
|
|
3
|
+
import { Fragment } from 'react';
|
|
4
4
|
import { css, jsx } from '@emotion/react';
|
|
5
5
|
import { useIntl } from 'react-intl-next';
|
|
6
6
|
import { LoadingButton } from '@atlaskit/button';
|
|
@@ -17,10 +17,6 @@ import { useValidateAqlText } from '../../../../hooks/useValidateAqlText';
|
|
|
17
17
|
import { aqlKey } from '../../../../types/assets/types';
|
|
18
18
|
import { FieldContainer } from '../styled';
|
|
19
19
|
import { searchInputMessages } from './messages';
|
|
20
|
-
|
|
21
|
-
/* Meta isn't exported in @atlaskit/form
|
|
22
|
-
Taken from packages/design-system/form/src/field.tsx */
|
|
23
|
-
|
|
24
20
|
const buttonBaseStyles = css({
|
|
25
21
|
display: 'flex',
|
|
26
22
|
height: '100%',
|
|
@@ -34,15 +30,14 @@ const AQLSupportDocumentLink = 'https://support.atlassian.com/jira-service-manag
|
|
|
34
30
|
const searchButtonStyles = css({
|
|
35
31
|
marginRight: "var(--ds-space-075, 6px)"
|
|
36
32
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (value && meta !== null && meta !== void 0 && meta.validating) {
|
|
33
|
+
const renderValidatorIcon = lastValidationResult => {
|
|
34
|
+
if (lastValidationResult.type === 'loading') {
|
|
40
35
|
return jsx(Spinner, {
|
|
41
36
|
size: "medium",
|
|
42
37
|
testId: "assets-datasource-modal--aql-validating"
|
|
43
38
|
});
|
|
44
39
|
}
|
|
45
|
-
if (
|
|
40
|
+
if (lastValidationResult.type === 'invalid') {
|
|
46
41
|
return jsx(CrossCircleIcon, {
|
|
47
42
|
label: "label",
|
|
48
43
|
primaryColor: "red",
|
|
@@ -50,7 +45,7 @@ const renderValidatorIcon = (value, error, meta) => {
|
|
|
50
45
|
testId: "assets-datasource-modal--aql-invalid"
|
|
51
46
|
});
|
|
52
47
|
}
|
|
53
|
-
if (
|
|
48
|
+
if (lastValidationResult.type === 'valid') {
|
|
54
49
|
return jsx(CheckCircleIcon, {
|
|
55
50
|
label: "label",
|
|
56
51
|
primaryColor: "green",
|
|
@@ -73,61 +68,23 @@ export const AqlSearchInput = ({
|
|
|
73
68
|
const {
|
|
74
69
|
formatMessage
|
|
75
70
|
} = useIntl();
|
|
76
|
-
const timeout = useRef();
|
|
77
|
-
const lastValue = useRef(value);
|
|
78
|
-
const lastResult = useRef(Promise.resolve(undefined));
|
|
79
|
-
const [message, setMessage] = useState(null);
|
|
80
71
|
const {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// Validation expects undefined when valid and a string as an error message when invalid
|
|
85
|
-
const handleValidation = useCallback(async newUnvalidatedQlQuery => {
|
|
86
|
-
if (!newUnvalidatedQlQuery) {
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
const validation = await validateAqlText(newUnvalidatedQlQuery);
|
|
90
|
-
setMessage(validation.message);
|
|
91
|
-
return validation.isValid ? undefined : 'invalid';
|
|
92
|
-
}, [validateAqlText]);
|
|
93
|
-
|
|
94
|
-
/* Debounce async validation for input, validation is also called on every field change
|
|
95
|
-
in a form so we need to also memoize. The async validate function is expected to either:
|
|
96
|
-
Immediately return a promise (which is then collected into an array, every single time validation is run),
|
|
97
|
-
or immediately return either undefined or an error message */
|
|
98
|
-
const debouncedMemoizedValidation = value => new Promise(resolve => {
|
|
99
|
-
if (timeout.current) {
|
|
100
|
-
timeout.current();
|
|
101
|
-
}
|
|
102
|
-
if (value !== lastValue.current) {
|
|
103
|
-
const timerId = setTimeout(() => {
|
|
104
|
-
lastValue.current = value;
|
|
105
|
-
lastResult.current = handleValidation(value);
|
|
106
|
-
resolve(lastResult.current);
|
|
107
|
-
}, SEARCH_DEBOUNCE_MS);
|
|
108
|
-
timeout.current = () => {
|
|
109
|
-
clearTimeout(timerId);
|
|
110
|
-
resolve('debouncing');
|
|
111
|
-
};
|
|
112
|
-
} else {
|
|
113
|
-
resolve(lastResult.current);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
72
|
+
debouncedValidation,
|
|
73
|
+
lastValidationResult
|
|
74
|
+
} = useValidateAqlText(workspaceId, value);
|
|
116
75
|
return jsx(FieldContainer, null, jsx(Field, {
|
|
117
76
|
name: aqlKey,
|
|
118
77
|
defaultValue: value,
|
|
119
|
-
validate:
|
|
78
|
+
validate: debouncedValidation
|
|
120
79
|
}, ({
|
|
121
|
-
fieldProps
|
|
122
|
-
meta,
|
|
123
|
-
error
|
|
80
|
+
fieldProps
|
|
124
81
|
}) => jsx(Fragment, null, jsx(Textfield, _extends({}, fieldProps, {
|
|
125
82
|
elemBeforeInput: jsx("span", {
|
|
126
83
|
style: {
|
|
127
84
|
paddingLeft: 6,
|
|
128
85
|
width: 24
|
|
129
86
|
}
|
|
130
|
-
}, renderValidatorIcon(
|
|
87
|
+
}, renderValidatorIcon(lastValidationResult)),
|
|
131
88
|
elemAfterInput: jsx(Fragment, null, jsx(Tooltip, {
|
|
132
89
|
content: formatMessage(searchInputMessages.helpTooltipText),
|
|
133
90
|
position: "bottom"
|
|
@@ -151,9 +108,9 @@ export const AqlSearchInput = ({
|
|
|
151
108
|
spacing: "none",
|
|
152
109
|
testId: "assets-datasource-modal--aql-search-button",
|
|
153
110
|
type: "submit",
|
|
154
|
-
isDisabled:
|
|
111
|
+
isDisabled: lastValidationResult.type !== 'valid'
|
|
155
112
|
})),
|
|
156
113
|
placeholder: formatMessage(searchInputMessages.placeholder),
|
|
157
114
|
testId: testId
|
|
158
|
-
})),
|
|
115
|
+
})), lastValidationResult.type === 'invalid' && lastValidationResult.error && jsx(ErrorMessage, null, lastValidationResult.error))));
|
|
159
116
|
};
|
|
@@ -24,7 +24,7 @@ export const AssetsSearchContainer = props => {
|
|
|
24
24
|
aql,
|
|
25
25
|
objectSchema
|
|
26
26
|
} = searchFormValues;
|
|
27
|
-
if (objectSchema) {
|
|
27
|
+
if (aql && objectSchema) {
|
|
28
28
|
fireEvent('ui.aqlEditor.searched', {});
|
|
29
29
|
// Pass the validated aql and object schema back to modal
|
|
30
30
|
onSearch(aql, objectSchema.value);
|
|
@@ -3,12 +3,15 @@ import { useCallback, useEffect, useRef } from 'react';
|
|
|
3
3
|
import { css, jsx } from '@emotion/react';
|
|
4
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
5
|
import { withAnalyticsContext } from '@atlaskit/analytics-next';
|
|
6
|
+
import { IntlMessagesProvider } from '@atlaskit/intl-messages-provider';
|
|
6
7
|
import { useDatasourceAnalyticsEvents } from '../../analytics';
|
|
7
8
|
import { packageMetaData } from '../../analytics/constants';
|
|
8
9
|
import { startUfoExperience } from '../../analytics/ufoExperiences';
|
|
9
10
|
import { useColumnPickerRenderedFailedUfoExperience } from '../../analytics/ufoExperiences/hooks/useColumnPickerRenderedFailedUfoExperience';
|
|
10
11
|
import { useDataRenderedUfoExperience } from '../../analytics/ufoExperiences/hooks/useDataRenderedUfoExperience';
|
|
12
|
+
import { fetchMessagesForLocale } from '../../common/utils/locale/fetch-messages-for-locale';
|
|
11
13
|
import { useDatasourceTableState } from '../../hooks/useDatasourceTableState';
|
|
14
|
+
import i18nEN from '../../i18n/en';
|
|
12
15
|
import { ScrollableContainerHeight } from '../../ui/issue-like-table/styled';
|
|
13
16
|
import { AccessRequired } from '../common/error-state/access-required';
|
|
14
17
|
import { LoadingError } from '../common/error-state/loading-error';
|
|
@@ -123,35 +126,35 @@ const DatasourceTableViewWithoutAnalytics = ({
|
|
|
123
126
|
onRefresh: reset
|
|
124
127
|
});
|
|
125
128
|
}
|
|
126
|
-
return (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
);
|
|
129
|
+
return jsx(IntlMessagesProvider, {
|
|
130
|
+
defaultMessages: i18nEN,
|
|
131
|
+
loaderFn: fetchMessagesForLocale
|
|
132
|
+
}, jsx("div", {
|
|
133
|
+
css: containerStyles,
|
|
134
|
+
className: "datasource-table"
|
|
135
|
+
}, hasColumns ? jsx(IssueLikeDataTableView, {
|
|
136
|
+
testId: 'datasource-table-view',
|
|
137
|
+
hasNextPage: hasNextPage,
|
|
138
|
+
items: responseItems,
|
|
139
|
+
onNextPage: onNextPage,
|
|
140
|
+
onLoadDatasourceDetails: loadDatasourceDetails,
|
|
141
|
+
status: status,
|
|
142
|
+
columns: columns,
|
|
143
|
+
visibleColumnKeys: visibleColumnKeys || defaultVisibleColumnKeys,
|
|
144
|
+
onVisibleColumnKeysChange: onVisibleColumnKeysChange,
|
|
145
|
+
columnCustomSizes: columnCustomSizes,
|
|
146
|
+
onColumnResize: onColumnResize,
|
|
147
|
+
scrollableContainerHeight: ScrollableContainerHeight,
|
|
148
|
+
parentContainerRenderInstanceId: tableRenderInstanceId,
|
|
149
|
+
extensionKey: extensionKey
|
|
150
|
+
}) : jsx(EmptyState, {
|
|
151
|
+
testId: "datasource-table-view-skeleton",
|
|
152
|
+
isCompact: true
|
|
153
|
+
}), jsx(TableFooter, {
|
|
154
|
+
itemCount: isDataReady ? totalCount : undefined,
|
|
155
|
+
onRefresh: forcedReset,
|
|
156
|
+
isLoading: !isDataReady || status === 'loading',
|
|
157
|
+
url: url
|
|
158
|
+
})));
|
|
156
159
|
};
|
|
157
160
|
export const DatasourceTableView = withAnalyticsContext(packageMetaData)(DatasourceTableViewWithoutAnalytics);
|
|
@@ -11,12 +11,12 @@ export const initialStateViewMessages = defineMessages({
|
|
|
11
11
|
defaultMessage: 'Beta'
|
|
12
12
|
},
|
|
13
13
|
searchDescriptionForBasicSearch: {
|
|
14
|
-
id: 'linkDataSource.modal-initial-state.searchDescription',
|
|
14
|
+
id: 'linkDataSource.modal-initial-state.searchDescription-basic',
|
|
15
15
|
description: 'The initial modal search state helper message displayed under the search title when basic search mode is selected',
|
|
16
16
|
defaultMessage: 'Search by keyword for issues to insert.'
|
|
17
17
|
},
|
|
18
18
|
searchDescriptionForJQLSearch: {
|
|
19
|
-
id: 'linkDataSource.modal-initial-state.searchDescription',
|
|
19
|
+
id: 'linkDataSource.modal-initial-state.searchDescription-jql',
|
|
20
20
|
description: 'The initial modal search state helper message displayed under the search title when JQL search mode is selected',
|
|
21
21
|
defaultMessage: 'Use JQL (Jira Query Language) to search for issues.'
|
|
22
22
|
},
|
|
@@ -6,6 +6,7 @@ import { FormattedMessage, FormattedNumber, useIntl } from 'react-intl-next';
|
|
|
6
6
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
7
|
import { withAnalyticsContext } from '@atlaskit/analytics-next';
|
|
8
8
|
import Button from '@atlaskit/button/standard-button';
|
|
9
|
+
import { IntlMessagesProvider } from '@atlaskit/intl-messages-provider';
|
|
9
10
|
import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
|
|
10
11
|
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
11
12
|
import LinkUrl from '@atlaskit/smart-card/link-url';
|
|
@@ -17,7 +18,9 @@ import { startUfoExperience } from '../../../analytics/ufoExperiences';
|
|
|
17
18
|
import { useColumnPickerRenderedFailedUfoExperience } from '../../../analytics/ufoExperiences/hooks/useColumnPickerRenderedFailedUfoExperience';
|
|
18
19
|
import { useDataRenderedUfoExperience } from '../../../analytics/ufoExperiences/hooks/useDataRenderedUfoExperience';
|
|
19
20
|
import { mapSearchMethod } from '../../../analytics/utils';
|
|
21
|
+
import { fetchMessagesForLocale } from '../../../common/utils/locale/fetch-messages-for-locale';
|
|
20
22
|
import { useDatasourceTableState } from '../../../hooks/useDatasourceTableState';
|
|
23
|
+
import i18nEN from '../../../i18n/en';
|
|
21
24
|
import { getAvailableJiraSites } from '../../../services/getAvailableJiraSites';
|
|
22
25
|
import { AccessRequired } from '../../common/error-state/access-required';
|
|
23
26
|
import { ModalLoadingError } from '../../common/error-state/modal-loading-error';
|
|
@@ -488,7 +491,10 @@ export const PlainJiraIssuesConfigModal = props => {
|
|
|
488
491
|
}
|
|
489
492
|
return issueLikeDataTableView;
|
|
490
493
|
}, [columns.length, currentSearchMethod, issueLikeDataTableView, jql, jqlUrl, resolvedWithNoResults, responseItems.length, retrieveUrlForSmartCardRender, selectedJiraSite === null || selectedJiraSite === void 0 ? void 0 : selectedJiraSite.url, status, urlBeingEdited]);
|
|
491
|
-
return jsx(
|
|
494
|
+
return jsx(IntlMessagesProvider, {
|
|
495
|
+
defaultMessages: i18nEN,
|
|
496
|
+
loaderFn: fetchMessagesForLocale
|
|
497
|
+
}, jsx(ModalTransition, null, jsx(Modal, {
|
|
492
498
|
testId: "jira-jql-datasource-modal",
|
|
493
499
|
onClose: onCancel,
|
|
494
500
|
width: "calc(100% - 80px)",
|
|
@@ -544,7 +550,7 @@ export const PlainJiraIssuesConfigModal = props => {
|
|
|
544
550
|
onClick: onInsertPressed,
|
|
545
551
|
isDisabled: isInsertDisabled,
|
|
546
552
|
testId: "jira-jql-datasource-modal--insert-button"
|
|
547
|
-
}, jsx(FormattedMessage, modalMessages.insertIssuesButtonText)))));
|
|
553
|
+
}, jsx(FormattedMessage, modalMessages.insertIssuesButtonText))))));
|
|
548
554
|
};
|
|
549
555
|
const analyticsContextAttributes = {
|
|
550
556
|
dataProvider: 'jira-issues'
|
|
@@ -1,77 +1,115 @@
|
|
|
1
1
|
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
2
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
3
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
4
|
-
import { useCallback, useState } from 'react';
|
|
4
|
+
import { useCallback, useRef, useState } from 'react';
|
|
5
5
|
import { useDatasourceAnalyticsEvents } from '../analytics';
|
|
6
6
|
import { validateAql } from '../services/cmdbService';
|
|
7
|
-
export var
|
|
8
|
-
|
|
7
|
+
export var SEARCH_DEBOUNCE = 350;
|
|
8
|
+
export var useValidateAqlText = function useValidateAqlText(workspaceId, initialValue) {
|
|
9
|
+
var timeout = useRef();
|
|
10
|
+
var lastValue = useRef('');
|
|
11
|
+
var lastResult = useRef(Promise.resolve(undefined));
|
|
12
|
+
var _useState = useState(initialValue.trim() === '' ? {
|
|
13
|
+
type: 'idle'
|
|
14
|
+
} : {
|
|
15
|
+
type: 'loading'
|
|
16
|
+
}),
|
|
9
17
|
_useState2 = _slicedToArray(_useState, 2),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var _useState3 = useState(false),
|
|
13
|
-
_useState4 = _slicedToArray(_useState3, 2),
|
|
14
|
-
isValidAqlText = _useState4[0],
|
|
15
|
-
setIsValidAqlText = _useState4[1];
|
|
16
|
-
var _useState5 = useState(),
|
|
17
|
-
_useState6 = _slicedToArray(_useState5, 2),
|
|
18
|
-
error = _useState6[0],
|
|
19
|
-
setError = _useState6[1];
|
|
18
|
+
lastValidationResult = _useState2[0],
|
|
19
|
+
setLastValidationResult = _useState2[1];
|
|
20
20
|
var _useDatasourceAnalyti = useDatasourceAnalyticsEvents(),
|
|
21
21
|
fireEvent = _useDatasourceAnalyti.fireEvent;
|
|
22
|
+
|
|
23
|
+
// We return undefined when valid and 'error' when invalid
|
|
22
24
|
var validateAqlText = useCallback( /*#__PURE__*/function () {
|
|
23
25
|
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(aql) {
|
|
24
|
-
var
|
|
26
|
+
var validateAqlResponse, _validateAqlResponse$, _validateAqlResponse$2;
|
|
25
27
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
26
28
|
while (1) switch (_context.prev = _context.next) {
|
|
27
29
|
case 0:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
_context.prev =
|
|
33
|
-
_context.next =
|
|
30
|
+
if (!(aql !== null && aql !== void 0 && aql.trim())) {
|
|
31
|
+
_context.next = 18;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
_context.prev = 1;
|
|
35
|
+
_context.next = 4;
|
|
34
36
|
return validateAql(workspaceId, {
|
|
35
37
|
qlQuery: aql
|
|
36
38
|
}, fireEvent);
|
|
37
|
-
case
|
|
39
|
+
case 4:
|
|
38
40
|
validateAqlResponse = _context.sent;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
_context.next = 16;
|
|
43
|
-
break;
|
|
44
|
-
case 13:
|
|
45
|
-
_context.prev = 13;
|
|
46
|
-
_context.t0 = _context["catch"](4);
|
|
47
|
-
if (_context.t0 instanceof Error) {
|
|
48
|
-
setError(_context.t0);
|
|
49
|
-
} else {
|
|
50
|
-
setError(new Error('Unexpected error occured'));
|
|
41
|
+
if (!validateAqlResponse.isValid) {
|
|
42
|
+
_context.next = 10;
|
|
43
|
+
break;
|
|
51
44
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return _context.finish(16);
|
|
56
|
-
case 19:
|
|
57
|
-
return _context.abrupt("return", {
|
|
58
|
-
isValid: isValid,
|
|
59
|
-
message: message
|
|
45
|
+
setLastValidationResult({
|
|
46
|
+
type: 'valid',
|
|
47
|
+
validatedAql: aql
|
|
60
48
|
});
|
|
49
|
+
return _context.abrupt("return", undefined);
|
|
50
|
+
case 10:
|
|
51
|
+
setLastValidationResult({
|
|
52
|
+
type: 'invalid',
|
|
53
|
+
error: (_validateAqlResponse$ = (_validateAqlResponse$2 = validateAqlResponse.errors) === null || _validateAqlResponse$2 === void 0 ? void 0 : _validateAqlResponse$2.iql) !== null && _validateAqlResponse$ !== void 0 ? _validateAqlResponse$ : ''
|
|
54
|
+
});
|
|
55
|
+
return _context.abrupt("return", 'error');
|
|
56
|
+
case 12:
|
|
57
|
+
_context.next = 18;
|
|
58
|
+
break;
|
|
59
|
+
case 14:
|
|
60
|
+
_context.prev = 14;
|
|
61
|
+
_context.t0 = _context["catch"](1);
|
|
62
|
+
setLastValidationResult({
|
|
63
|
+
type: 'invalid',
|
|
64
|
+
error: ''
|
|
65
|
+
});
|
|
66
|
+
return _context.abrupt("return", 'error');
|
|
67
|
+
case 18:
|
|
68
|
+
setLastValidationResult({
|
|
69
|
+
type: 'idle'
|
|
70
|
+
});
|
|
71
|
+
return _context.abrupt("return", undefined);
|
|
61
72
|
case 20:
|
|
62
73
|
case "end":
|
|
63
74
|
return _context.stop();
|
|
64
75
|
}
|
|
65
|
-
}, _callee, null, [[
|
|
76
|
+
}, _callee, null, [[1, 14]]);
|
|
66
77
|
}));
|
|
67
78
|
return function (_x) {
|
|
68
79
|
return _ref.apply(this, arguments);
|
|
69
80
|
};
|
|
70
81
|
}(), [workspaceId, fireEvent]);
|
|
82
|
+
|
|
83
|
+
/* Debounce async validation for input, validation is also called on every field change
|
|
84
|
+
in a form so we need to also memoize. The async validate function is expected to either:
|
|
85
|
+
Immediately return a promise (which is then collected into an array, every single time validation is run),
|
|
86
|
+
or immediately return either undefined or an error message */
|
|
87
|
+
var debouncedValidation = function debouncedValidation(value) {
|
|
88
|
+
return new Promise(function (resolve) {
|
|
89
|
+
if (timeout.current) {
|
|
90
|
+
timeout.current();
|
|
91
|
+
}
|
|
92
|
+
if (value !== lastValue.current) {
|
|
93
|
+
var timerId = setTimeout(function () {
|
|
94
|
+
setLastValidationResult({
|
|
95
|
+
type: 'loading'
|
|
96
|
+
});
|
|
97
|
+
lastValue.current = value;
|
|
98
|
+
lastResult.current = validateAqlText(value);
|
|
99
|
+
resolve(lastResult.current);
|
|
100
|
+
}, SEARCH_DEBOUNCE);
|
|
101
|
+
timeout.current = function () {
|
|
102
|
+
clearTimeout(timerId);
|
|
103
|
+
resolve('debouncing');
|
|
104
|
+
};
|
|
105
|
+
} else {
|
|
106
|
+
resolve(lastResult.current);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
71
110
|
return {
|
|
72
|
-
|
|
111
|
+
debouncedValidation: debouncedValidation,
|
|
73
112
|
validateAqlText: validateAqlText,
|
|
74
|
-
|
|
75
|
-
validateAqlTextError: error
|
|
113
|
+
lastValidationResult: lastValidationResult
|
|
76
114
|
};
|
|
77
115
|
};
|