@atlaskit/link-datasource 1.15.2 → 1.15.4
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 +12 -0
- package/dist/cjs/analytics/constants.js +1 -1
- package/dist/cjs/hooks/useAssetsClient.js +71 -27
- package/dist/cjs/services/cmdbService.js +128 -28
- package/dist/cjs/services/cmdbService.utils.js +64 -0
- package/dist/cjs/ui/assets-modal/modal/index.js +73 -9
- package/dist/cjs/ui/assets-modal/modal/render-assets-content/index.js +25 -12
- package/dist/cjs/ui/assets-modal/search-container/index.js +2 -1
- package/dist/cjs/ui/assets-modal/search-container/object-schema-select/index.js +21 -63
- package/dist/cjs/ui/issue-like-table/render-type/date-time/index.js +7 -1
- package/dist/es2019/analytics/constants.js +1 -1
- package/dist/es2019/hooks/useAssetsClient.js +39 -15
- package/dist/es2019/services/cmdbService.js +60 -14
- package/dist/es2019/services/cmdbService.utils.js +39 -0
- package/dist/es2019/ui/assets-modal/modal/index.js +70 -9
- package/dist/es2019/ui/assets-modal/modal/render-assets-content/index.js +24 -9
- package/dist/es2019/ui/assets-modal/search-container/index.js +2 -1
- package/dist/es2019/ui/assets-modal/search-container/object-schema-select/index.js +2 -25
- package/dist/es2019/ui/issue-like-table/render-type/date-time/index.js +7 -1
- package/dist/esm/analytics/constants.js +1 -1
- package/dist/esm/hooks/useAssetsClient.js +72 -28
- package/dist/esm/services/cmdbService.js +128 -28
- package/dist/esm/services/cmdbService.utils.js +57 -0
- package/dist/esm/ui/assets-modal/modal/index.js +73 -9
- package/dist/esm/ui/assets-modal/modal/render-assets-content/index.js +24 -9
- package/dist/esm/ui/assets-modal/search-container/index.js +2 -1
- package/dist/esm/ui/assets-modal/search-container/object-schema-select/index.js +18 -60
- package/dist/esm/ui/issue-like-table/render-type/date-time/index.js +7 -1
- package/dist/types/hooks/useAssetsClient.d.ts +5 -1
- package/dist/types/services/cmdbService.utils.d.ts +9 -0
- package/dist/types/ui/assets-modal/modal/render-assets-content/index.d.ts +3 -2
- package/dist/types/ui/assets-modal/search-container/index.d.ts +1 -0
- package/dist/types/ui/assets-modal/search-container/object-schema-select/index.d.ts +2 -1
- package/dist/types-ts4.5/hooks/useAssetsClient.d.ts +5 -1
- package/dist/types-ts4.5/services/cmdbService.utils.d.ts +9 -0
- package/dist/types-ts4.5/ui/assets-modal/modal/render-assets-content/index.d.ts +3 -2
- package/dist/types-ts4.5/ui/assets-modal/search-container/index.d.ts +1 -0
- package/dist/types-ts4.5/ui/assets-modal/search-container/object-schema-select/index.d.ts +2 -1
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@ var AssetsSearchContainer = exports.AssetsSearchContainer = function AssetsSearc
|
|
|
36
36
|
return (0, _react.jsx)(_form.default, {
|
|
37
37
|
onSubmit: onFormSubmit
|
|
38
38
|
}, function (_ref) {
|
|
39
|
-
var _initialSearchData$ob, _initialSearchData$aq;
|
|
39
|
+
var _initialSearchData$ob, _initialSearchData$ob2, _initialSearchData$aq;
|
|
40
40
|
var formProps = _ref.formProps;
|
|
41
41
|
return (0, _react.jsx)(_styled.FormContainer, (0, _extends2.default)({}, formProps, {
|
|
42
42
|
id: SEARCH_FORM_ID
|
|
@@ -45,6 +45,7 @@ var AssetsSearchContainer = exports.AssetsSearchContainer = function AssetsSearc
|
|
|
45
45
|
}, modalTitle, (0, _react.jsx)(_styled.SchemaSelectContainer, null, (0, _react.jsx)(_objectSchemaSelect.AssetsObjectSchemaSelect, {
|
|
46
46
|
value: (_initialSearchData$ob = initialSearchData.objectSchema) !== null && _initialSearchData$ob !== void 0 ? _initialSearchData$ob : undefined,
|
|
47
47
|
workspaceId: workspaceId,
|
|
48
|
+
initialObjectSchemas: (_initialSearchData$ob2 = initialSearchData.objectSchemas) !== null && _initialSearchData$ob2 !== void 0 ? _initialSearchData$ob2 : undefined,
|
|
48
49
|
classNamePrefix: "assets-datasource-modal--object-schema-select"
|
|
49
50
|
}))), (0, _react.jsx)(_styled.FormRowContainer, null, (0, _react.jsx)(_aqlSearchInput.AqlSearchInput, {
|
|
50
51
|
value: (_initialSearchData$aq = initialSearchData.aql) !== null && _initialSearchData$aq !== void 0 ? _initialSearchData$aq : DEFAULT_AQL_QUERY,
|
|
@@ -9,16 +9,13 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
|
|
|
9
9
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
10
10
|
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
11
11
|
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
12
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
13
12
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
14
|
-
var _react = require("react");
|
|
15
|
-
var _react2 = require("@emotion/react");
|
|
13
|
+
var _react = require("@emotion/react");
|
|
16
14
|
var _debouncePromise = _interopRequireDefault(require("debounce-promise"));
|
|
17
15
|
var _reactIntlNext = require("react-intl-next");
|
|
18
16
|
var _form = require("@atlaskit/form");
|
|
19
17
|
var _select = require("@atlaskit/select");
|
|
20
18
|
var _constants = require("@atlaskit/theme/constants");
|
|
21
|
-
var _analytics = require("../../../../analytics");
|
|
22
19
|
var _useObjectSchemas2 = require("../../../../hooks/useObjectSchemas");
|
|
23
20
|
var _types = require("../../../../types/assets/types");
|
|
24
21
|
var _styled = require("../styled");
|
|
@@ -51,74 +48,35 @@ var selectInAModalStyleFixProps = exports.selectInAModalStyleFixProps = {
|
|
|
51
48
|
var AssetsObjectSchemaSelect = exports.AssetsObjectSchemaSelect = function AssetsObjectSchemaSelect(_ref) {
|
|
52
49
|
var value = _ref.value,
|
|
53
50
|
workspaceId = _ref.workspaceId,
|
|
51
|
+
initialObjectSchemas = _ref.initialObjectSchemas,
|
|
54
52
|
_ref$classNamePrefix = _ref.classNamePrefix,
|
|
55
53
|
classNamePrefix = _ref$classNamePrefix === void 0 ? 'assets-datasource-modal--object-schema-select' : _ref$classNamePrefix;
|
|
56
|
-
var _useState = (0, _react.useState)(null),
|
|
57
|
-
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
58
|
-
defaultOptions = _useState2[0],
|
|
59
|
-
setDefaultOptions = _useState2[1];
|
|
60
|
-
var _useDatasourceAnalyti = (0, _analytics.useDatasourceAnalyticsEvents)(),
|
|
61
|
-
fireEvent = _useDatasourceAnalyti.fireEvent;
|
|
62
54
|
var _useIntl = (0, _reactIntlNext.useIntl)(),
|
|
63
55
|
formatMessage = _useIntl.formatMessage;
|
|
64
56
|
var _useObjectSchemas = (0, _useObjectSchemas2.useObjectSchemas)(workspaceId),
|
|
65
57
|
fetchObjectSchemas = _useObjectSchemas.fetchObjectSchemas,
|
|
66
58
|
objectSchemasLoading = _useObjectSchemas.objectSchemasLoading;
|
|
67
59
|
var selectedObjectSchema = value ? (0, _utils.objectSchemaToSelectOption)(value) : undefined;
|
|
68
|
-
(0, _react.useEffect)(function () {
|
|
69
|
-
var fetchInitialData = /*#__PURE__*/function () {
|
|
70
|
-
var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
71
|
-
var _yield$fetchObjectSch, objectSchemas, totalObjectSchemas;
|
|
72
|
-
return _regenerator.default.wrap(function _callee$(_context) {
|
|
73
|
-
while (1) switch (_context.prev = _context.next) {
|
|
74
|
-
case 0:
|
|
75
|
-
_context.next = 2;
|
|
76
|
-
return fetchObjectSchemas('');
|
|
77
|
-
case 2:
|
|
78
|
-
_yield$fetchObjectSch = _context.sent;
|
|
79
|
-
objectSchemas = _yield$fetchObjectSch.objectSchemas;
|
|
80
|
-
totalObjectSchemas = _yield$fetchObjectSch.totalObjectSchemas;
|
|
81
|
-
// We only want to send modal ready event once after we've fetched the schema count
|
|
82
|
-
fireEvent('ui.modal.ready.datasource', {
|
|
83
|
-
schemasCount: totalObjectSchemas !== null && totalObjectSchemas !== void 0 ? totalObjectSchemas : 0,
|
|
84
|
-
instancesCount: null
|
|
85
|
-
});
|
|
86
|
-
setDefaultOptions(mapObjectSchemasToOptions(objectSchemas));
|
|
87
|
-
case 7:
|
|
88
|
-
case "end":
|
|
89
|
-
return _context.stop();
|
|
90
|
-
}
|
|
91
|
-
}, _callee);
|
|
92
|
-
}));
|
|
93
|
-
return function fetchInitialData() {
|
|
94
|
-
return _ref2.apply(this, arguments);
|
|
95
|
-
};
|
|
96
|
-
}();
|
|
97
|
-
if (defaultOptions === null) {
|
|
98
|
-
fetchInitialData();
|
|
99
|
-
}
|
|
100
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
101
|
-
}, []);
|
|
102
60
|
var loadOptions = /*#__PURE__*/function () {
|
|
103
|
-
var
|
|
104
|
-
var _yield$
|
|
105
|
-
return _regenerator.default.wrap(function
|
|
106
|
-
while (1) switch (
|
|
61
|
+
var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(inputValue) {
|
|
62
|
+
var _yield$fetchObjectSch, objectSchemas;
|
|
63
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
64
|
+
while (1) switch (_context.prev = _context.next) {
|
|
107
65
|
case 0:
|
|
108
|
-
|
|
66
|
+
_context.next = 2;
|
|
109
67
|
return fetchObjectSchemas(inputValue);
|
|
110
68
|
case 2:
|
|
111
|
-
_yield$
|
|
112
|
-
objectSchemas = _yield$
|
|
113
|
-
return
|
|
69
|
+
_yield$fetchObjectSch = _context.sent;
|
|
70
|
+
objectSchemas = _yield$fetchObjectSch.objectSchemas;
|
|
71
|
+
return _context.abrupt("return", mapObjectSchemasToOptions(objectSchemas));
|
|
114
72
|
case 5:
|
|
115
73
|
case "end":
|
|
116
|
-
return
|
|
74
|
+
return _context.stop();
|
|
117
75
|
}
|
|
118
|
-
},
|
|
76
|
+
}, _callee);
|
|
119
77
|
}));
|
|
120
78
|
return function loadOptions(_x) {
|
|
121
|
-
return
|
|
79
|
+
return _ref2.apply(this, arguments);
|
|
122
80
|
};
|
|
123
81
|
}();
|
|
124
82
|
var debouncedLoadOptions = (0, _debouncePromise.default)(loadOptions, SEARCH_DEBOUNCE_MS);
|
|
@@ -128,22 +86,22 @@ var AssetsObjectSchemaSelect = exports.AssetsObjectSchemaSelect = function Asset
|
|
|
128
86
|
}
|
|
129
87
|
return undefined;
|
|
130
88
|
};
|
|
131
|
-
return (0,
|
|
89
|
+
return (0, _react.jsx)(_styled.FieldContainer, null, (0, _react.jsx)(_form.Field, {
|
|
132
90
|
name: _types.objectSchemaKey,
|
|
133
91
|
defaultValue: selectedObjectSchema,
|
|
134
92
|
validate: function validate(value) {
|
|
135
93
|
return validateSchema(value);
|
|
136
94
|
}
|
|
137
|
-
}, function (
|
|
138
|
-
var
|
|
139
|
-
_onChange =
|
|
140
|
-
onFocus =
|
|
141
|
-
restFieldProps = (0, _objectWithoutProperties2.default)(
|
|
142
|
-
return (0,
|
|
95
|
+
}, function (_ref3) {
|
|
96
|
+
var _ref3$fieldProps = _ref3.fieldProps,
|
|
97
|
+
_onChange = _ref3$fieldProps.onChange,
|
|
98
|
+
onFocus = _ref3$fieldProps.onFocus,
|
|
99
|
+
restFieldProps = (0, _objectWithoutProperties2.default)(_ref3$fieldProps, _excluded);
|
|
100
|
+
return (0, _react.jsx)(_select.AsyncSelect, (0, _extends2.default)({
|
|
143
101
|
autoFocus: true,
|
|
144
102
|
classNamePrefix: classNamePrefix,
|
|
145
103
|
isLoading: objectSchemasLoading,
|
|
146
|
-
defaultOptions:
|
|
104
|
+
defaultOptions: mapObjectSchemasToOptions(initialObjectSchemas),
|
|
147
105
|
isSearchable: true,
|
|
148
106
|
loadOptions: debouncedLoadOptions,
|
|
149
107
|
placeholder: formatMessage(_messages.objectSchemaSelectMessages.placeholder),
|
|
@@ -32,8 +32,14 @@ var DateTimeRenderType = function DateTimeRenderType(_ref) {
|
|
|
32
32
|
testId = _ref$testId === void 0 ? DATETIME_TYPE_TEST_ID : _ref$testId,
|
|
33
33
|
_ref$display = _ref.display,
|
|
34
34
|
display = _ref$display === void 0 ? 'datetime' : _ref$display;
|
|
35
|
-
var date = new Date(value);
|
|
36
35
|
var intl = (0, _reactIntlNext.useIntl)();
|
|
36
|
+
/* In some cases we get a value of `2023-12-20` which when parsed by JS assumes meantime timezone, causing the date
|
|
37
|
+
to be one day off in some timezones. We want it to display the date without converting timezones and a solution
|
|
38
|
+
is to replace the hyphens with slashes. So it should be 20th Dec regardless of the timezone in this case.
|
|
39
|
+
See https://stackoverflow.com/a/31732581
|
|
40
|
+
*/
|
|
41
|
+
var dateValue = /^\d{4}-\d{2}-\d{2}$/.exec(value) ? value.replace(/-/g, '/') : value;
|
|
42
|
+
var date = new Date(dateValue);
|
|
37
43
|
if (!value || isNaN(date.getTime())) {
|
|
38
44
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null);
|
|
39
45
|
}
|
|
@@ -1,32 +1,52 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import { fetchObjectSchema, getWorkspaceId } from '../services/cmdbService';
|
|
2
|
+
import { fetchObjectSchema, fetchObjectSchemas, getWorkspaceId } from '../services/cmdbService';
|
|
3
|
+
const handleAssetsClientErrors = (errorSetter, error) => {
|
|
4
|
+
if (error instanceof Error) {
|
|
5
|
+
errorSetter(error);
|
|
6
|
+
} else {
|
|
7
|
+
errorSetter(new Error('Unexpected error occured'));
|
|
8
|
+
}
|
|
9
|
+
};
|
|
3
10
|
export const useAssetsClient = initialParameters => {
|
|
4
11
|
const [loading, setLoading] = useState(false);
|
|
5
12
|
const [workspaceId, setWorkspaceId] = useState();
|
|
6
|
-
const [
|
|
7
|
-
const [
|
|
13
|
+
const [workspaceError, setWorkspaceError] = useState();
|
|
14
|
+
const [existingObjectSchema, setExistingObjectSchema] = useState();
|
|
15
|
+
const [existingObjectSchemaError, setExistingObjectSchemaError] = useState();
|
|
16
|
+
const [objectSchemas, setObjectSchemas] = useState();
|
|
17
|
+
const [totalObjectSchemas, setTotalObjectSchemas] = useState();
|
|
18
|
+
const [objectSchemasError, setObjectSchemasError] = useState();
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* We wrap this in nested try/catch blocks because we want to handle
|
|
22
|
+
* workspaceError/existingObjectSchemaError/objectSchemasError differently
|
|
23
|
+
* if we need to implement more initial data fetching/errors we should look at a store
|
|
24
|
+
*/
|
|
8
25
|
useEffect(() => {
|
|
9
26
|
(async () => {
|
|
10
27
|
setLoading(true);
|
|
11
|
-
|
|
28
|
+
setWorkspaceError(undefined);
|
|
12
29
|
try {
|
|
13
30
|
const workspaceId = await getWorkspaceId();
|
|
14
31
|
setWorkspaceId(workspaceId);
|
|
15
|
-
// Check schema from initial parameters still exists and fetch name for schema select
|
|
32
|
+
// Check schema from initial parameters still exists and fetch name/permissions for schema select
|
|
16
33
|
if (initialParameters !== null && initialParameters !== void 0 && initialParameters.schemaId) {
|
|
17
34
|
try {
|
|
18
35
|
const fetchedObjectSchema = await fetchObjectSchema(workspaceId, initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.schemaId);
|
|
19
|
-
|
|
20
|
-
} catch {
|
|
21
|
-
|
|
36
|
+
setExistingObjectSchema(fetchedObjectSchema);
|
|
37
|
+
} catch (fetchObjectSchemaError) {
|
|
38
|
+
handleAssetsClientErrors(setExistingObjectSchemaError, fetchObjectSchemaError);
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
try {
|
|
42
|
+
const fetchedObjectSchemasResponse = await fetchObjectSchemas(workspaceId);
|
|
43
|
+
setObjectSchemas(fetchedObjectSchemasResponse.values);
|
|
44
|
+
setTotalObjectSchemas(fetchedObjectSchemasResponse.total);
|
|
45
|
+
} catch (fetchObjectSchemasError) {
|
|
46
|
+
handleAssetsClientErrors(setObjectSchemasError, fetchObjectSchemasError);
|
|
29
47
|
}
|
|
48
|
+
} catch (getWorkspaceIdError) {
|
|
49
|
+
handleAssetsClientErrors(setWorkspaceError, getWorkspaceIdError);
|
|
30
50
|
} finally {
|
|
31
51
|
setLoading(false);
|
|
32
52
|
}
|
|
@@ -34,8 +54,12 @@ export const useAssetsClient = initialParameters => {
|
|
|
34
54
|
}, [initialParameters]);
|
|
35
55
|
return {
|
|
36
56
|
workspaceId,
|
|
37
|
-
workspaceError
|
|
38
|
-
|
|
57
|
+
workspaceError,
|
|
58
|
+
existingObjectSchema,
|
|
59
|
+
existingObjectSchemaError,
|
|
60
|
+
objectSchemas,
|
|
61
|
+
totalObjectSchemas,
|
|
62
|
+
objectSchemasError,
|
|
39
63
|
assetsClientLoading: loading
|
|
40
64
|
};
|
|
41
65
|
};
|
|
@@ -1,29 +1,75 @@
|
|
|
1
1
|
import { request } from '@atlaskit/linking-common';
|
|
2
|
+
import { FetchError, getStatusCodeGroup, mapFetchErrors, PermissionError } from './cmdbService.utils';
|
|
2
3
|
export const getWorkspaceId = async () => {
|
|
3
|
-
var _workspaceDetailsResp;
|
|
4
4
|
const url = '/rest/servicedesk/cmdb/latest/workspace';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
try {
|
|
6
|
+
var _workspaceDetailsResp;
|
|
7
|
+
const workspaceDetailsResponse = await request('get', url, undefined, undefined, [200, 201, 202, 203, 204]);
|
|
8
|
+
if (!((_workspaceDetailsResp = workspaceDetailsResponse.results) !== null && _workspaceDetailsResp !== void 0 && _workspaceDetailsResp.length)) {
|
|
9
|
+
throw new PermissionError('No workspace results found');
|
|
10
|
+
}
|
|
11
|
+
return workspaceDetailsResponse.results[0].id;
|
|
12
|
+
} catch (err) {
|
|
13
|
+
let error = mapFetchErrors(err);
|
|
14
|
+
if (error instanceof FetchError) {
|
|
15
|
+
// TODO Fire error operational event for workspace here before remapping to PermissionError
|
|
16
|
+
// Only 429 and 5xx errors will be treated as FetchErrors otherwise PermissionError
|
|
17
|
+
if (getStatusCodeGroup(error) !== '5xx' && error.statusCode !== 429) {
|
|
18
|
+
error = new PermissionError('Failed to fetch workspace');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
8
22
|
}
|
|
9
|
-
return workspaceDetailsResponse.results[0].id;
|
|
10
23
|
};
|
|
11
|
-
export const validateAql = (workspaceId, data) => {
|
|
24
|
+
export const validateAql = async (workspaceId, data) => {
|
|
12
25
|
const url = `/gateway/api/jsm/assets/workspace/${workspaceId}/v1/aql/validate`;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
26
|
+
try {
|
|
27
|
+
return await request('post', url, {
|
|
28
|
+
qlQuery: data.qlQuery,
|
|
29
|
+
context: 'SMART_LINKS'
|
|
30
|
+
}, undefined, [200, 201, 202, 203, 204]);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
let error = mapFetchErrors(err);
|
|
33
|
+
if (error instanceof FetchError) {
|
|
34
|
+
// TODO Fire error operational event for aql here before remapping to PermissionError
|
|
35
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
36
|
+
error = new PermissionError('Failed to fetch object schemas');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
17
41
|
};
|
|
18
|
-
export const fetchObjectSchema = (workspaceId, schemaId) => {
|
|
42
|
+
export const fetchObjectSchema = async (workspaceId, schemaId) => {
|
|
19
43
|
const url = `/gateway/api/jsm/assets/workspace/${workspaceId}/v1/objectschema/${schemaId}`;
|
|
20
|
-
|
|
44
|
+
try {
|
|
45
|
+
return await request('get', url, undefined, undefined, [200, 201, 202, 203, 204]);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
let error = mapFetchErrors(err);
|
|
48
|
+
if (error instanceof FetchError) {
|
|
49
|
+
// TODO Fire error operational event for object schema here before remapping to PermissionError
|
|
50
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
51
|
+
error = new PermissionError('Failed to fetch object schemas');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
21
56
|
};
|
|
22
|
-
export const fetchObjectSchemas = (workspaceId, query) => {
|
|
57
|
+
export const fetchObjectSchemas = async (workspaceId, query) => {
|
|
23
58
|
const queryParams = new URLSearchParams();
|
|
24
59
|
queryParams.set('maxResults', '20');
|
|
25
60
|
queryParams.set('includeCounts', 'false');
|
|
26
61
|
query && queryParams.set('query', query);
|
|
27
62
|
const url = `/gateway/api/jsm/assets/workspace/${workspaceId}/v1/objectschema/list?${queryParams}`;
|
|
28
|
-
|
|
63
|
+
try {
|
|
64
|
+
return await request('get', url, undefined, undefined, [200, 201, 202, 203, 204]);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
let error = mapFetchErrors(err);
|
|
67
|
+
if (error instanceof FetchError) {
|
|
68
|
+
// TODO Fire error operational event for object schemas here before remapping to PermissionError
|
|
69
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
70
|
+
error = new PermissionError('Failed to fetch object schemas');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
29
75
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const mapFetchErrors = error => {
|
|
2
|
+
if (error instanceof Response && !error.ok) {
|
|
3
|
+
return new FetchError(error.status, `Error server response: ${error.status}`);
|
|
4
|
+
}
|
|
5
|
+
return error;
|
|
6
|
+
};
|
|
7
|
+
export const getStatusCodeGroup = error => {
|
|
8
|
+
if (error instanceof FetchError) {
|
|
9
|
+
const {
|
|
10
|
+
statusCode
|
|
11
|
+
} = error;
|
|
12
|
+
if (statusCode >= 100 && statusCode < 200) {
|
|
13
|
+
return '1xx';
|
|
14
|
+
}
|
|
15
|
+
if (statusCode >= 300 && statusCode < 400) {
|
|
16
|
+
return '3xx';
|
|
17
|
+
}
|
|
18
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
19
|
+
return '4xx';
|
|
20
|
+
}
|
|
21
|
+
if (statusCode >= 500 && statusCode < 600) {
|
|
22
|
+
return '5xx';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return 'unknown';
|
|
26
|
+
};
|
|
27
|
+
export class PermissionError extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'PermissionError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class FetchError extends Error {
|
|
34
|
+
constructor(statusCode, message) {
|
|
35
|
+
super(message || `Fetch call failed with status code: ${statusCode}`);
|
|
36
|
+
this.name = 'FetchError';
|
|
37
|
+
this.statusCode = statusCode;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -18,6 +18,8 @@ import { fetchMessagesForLocale } from '../../../common/utils/locale/fetch-messa
|
|
|
18
18
|
import { useAssetsClient } from '../../../hooks/useAssetsClient';
|
|
19
19
|
import { useDatasourceTableState } from '../../../hooks/useDatasourceTableState';
|
|
20
20
|
import i18nEN from '../../../i18n/en';
|
|
21
|
+
import { PermissionError } from '../../../services/cmdbService.utils';
|
|
22
|
+
import { AccessRequired } from '../../../ui/common/error-state/access-required';
|
|
21
23
|
import { ModalLoadingError } from '../../common/error-state/modal-loading-error';
|
|
22
24
|
import { AssetsSearchContainer } from '../search-container';
|
|
23
25
|
import { AssetsSearchContainerLoading } from '../search-container/loading-state';
|
|
@@ -28,6 +30,9 @@ const modalBodyWrapperStyles = css({
|
|
|
28
30
|
height: '420px',
|
|
29
31
|
overflow: 'auto'
|
|
30
32
|
});
|
|
33
|
+
const modalBodyErrorWrapperStyles = css({
|
|
34
|
+
alignItems: 'center'
|
|
35
|
+
});
|
|
31
36
|
const AssetsModalTitle = jsx(ModalTitle, null, jsx(FormattedMessage, modalMessages.insertObjectsTitle));
|
|
32
37
|
const PlainAssetsConfigModal = props => {
|
|
33
38
|
const {
|
|
@@ -41,20 +46,53 @@ const PlainAssetsConfigModal = props => {
|
|
|
41
46
|
const [schemaId, setSchemaId] = useState(initialParameters === null || initialParameters === void 0 ? void 0 : initialParameters.schemaId);
|
|
42
47
|
const [visibleColumnKeys, setVisibleColumnKeys] = useState(initialVisibleColumnKeys);
|
|
43
48
|
const [isNewSearch, setIsNewSearch] = useState(false);
|
|
49
|
+
const [errorState, setErrorState] = useState();
|
|
44
50
|
const {
|
|
45
51
|
fireEvent
|
|
46
52
|
} = useDatasourceAnalyticsEvents();
|
|
47
53
|
const {
|
|
48
54
|
current: modalRenderInstanceId
|
|
49
55
|
} = useRef(uuidv4());
|
|
50
|
-
|
|
51
|
-
// If a workspaceError occurs this is a critical error
|
|
52
56
|
const {
|
|
53
57
|
workspaceId,
|
|
54
58
|
workspaceError,
|
|
55
|
-
|
|
59
|
+
existingObjectSchema,
|
|
60
|
+
existingObjectSchemaError,
|
|
61
|
+
objectSchemas,
|
|
62
|
+
objectSchemasError,
|
|
63
|
+
totalObjectSchemas,
|
|
56
64
|
assetsClientLoading
|
|
57
65
|
} = useAssetsClient(initialParameters);
|
|
66
|
+
|
|
67
|
+
/* ------------------------------ PERMISSIONS ------------------------------ */
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (workspaceError) {
|
|
70
|
+
// If a workspaceError occurs this is a critical error
|
|
71
|
+
if (workspaceError instanceof PermissionError) {
|
|
72
|
+
setErrorState('permission');
|
|
73
|
+
} else {
|
|
74
|
+
setErrorState('network');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}, [workspaceError]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (objectSchemasError) {
|
|
80
|
+
// We only care about permission errors for objectSchemas fetching as the user can retry this action
|
|
81
|
+
if (objectSchemasError instanceof PermissionError) {
|
|
82
|
+
setErrorState('permission');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}, [objectSchemasError]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (existingObjectSchemaError) {
|
|
88
|
+
// We only care about permission errors for existingObjectSchema fetching as the user can retry this action
|
|
89
|
+
if (existingObjectSchemaError instanceof PermissionError) {
|
|
90
|
+
setErrorState('permission');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}, [existingObjectSchemaError]);
|
|
94
|
+
/* ------------------------------ END PERMISSIONS ------------------------------ */
|
|
95
|
+
|
|
58
96
|
const parameters = useMemo(() => ({
|
|
59
97
|
aql: aql || '',
|
|
60
98
|
schemaId: schemaId || '',
|
|
@@ -90,6 +128,15 @@ const PlainAssetsConfigModal = props => {
|
|
|
90
128
|
destinationObjectTypes: destinationObjectTypes
|
|
91
129
|
};
|
|
92
130
|
}, [destinationObjectTypes, extensionKey]);
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
// We only want to send modal ready event once after we've fetched the schema count
|
|
133
|
+
if (totalObjectSchemas !== undefined) {
|
|
134
|
+
fireEvent('ui.modal.ready.datasource', {
|
|
135
|
+
schemasCount: totalObjectSchemas,
|
|
136
|
+
instancesCount: null
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}, [fireEvent, totalObjectSchemas]);
|
|
93
140
|
useEffect(() => {
|
|
94
141
|
fireEvent('screen.datasourceModalDialog.viewed', {});
|
|
95
142
|
}, [fireEvent]);
|
|
@@ -144,7 +191,7 @@ const PlainAssetsConfigModal = props => {
|
|
|
144
191
|
setVisibleColumnKeys(defaultVisibleColumnKeys);
|
|
145
192
|
}
|
|
146
193
|
}, [defaultVisibleColumnKeys, isNewSearch]);
|
|
147
|
-
const isDisabled = !!
|
|
194
|
+
const isDisabled = !!errorState || status !== 'resolved' || assetsClientLoading || !aql || !schemaId;
|
|
148
195
|
const retrieveUrlForSmartCardRender = useCallback(() => {
|
|
149
196
|
var _data$key, _data$key$data;
|
|
150
197
|
const [data] = responseItems;
|
|
@@ -232,8 +279,20 @@ const PlainAssetsConfigModal = props => {
|
|
|
232
279
|
setIsNewSearch(true);
|
|
233
280
|
}
|
|
234
281
|
}, [aql, reset, schemaId, status]);
|
|
282
|
+
const renderErrorState = useCallback(() => {
|
|
283
|
+
if (errorState) {
|
|
284
|
+
switch (errorState) {
|
|
285
|
+
case 'permission':
|
|
286
|
+
return jsx(AccessRequired, null);
|
|
287
|
+
case 'network':
|
|
288
|
+
return jsx(ModalLoadingError, null);
|
|
289
|
+
default:
|
|
290
|
+
return jsx(ModalLoadingError, null);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}, [errorState]);
|
|
235
294
|
const renderModalTitleContent = useCallback(() => {
|
|
236
|
-
if (
|
|
295
|
+
if (errorState) {
|
|
237
296
|
return undefined;
|
|
238
297
|
} else {
|
|
239
298
|
if (!workspaceId || assetsClientLoading) {
|
|
@@ -245,14 +304,15 @@ const PlainAssetsConfigModal = props => {
|
|
|
245
304
|
workspaceId: workspaceId,
|
|
246
305
|
initialSearchData: {
|
|
247
306
|
aql,
|
|
248
|
-
objectSchema
|
|
307
|
+
objectSchema: existingObjectSchema,
|
|
308
|
+
objectSchemas
|
|
249
309
|
},
|
|
250
310
|
onSearch: handleOnSearch,
|
|
251
311
|
modalTitle: AssetsModalTitle,
|
|
252
312
|
isSearching: status === 'loading'
|
|
253
313
|
});
|
|
254
314
|
}
|
|
255
|
-
}, [
|
|
315
|
+
}, [errorState, workspaceId, assetsClientLoading, aql, existingObjectSchema, objectSchemas, handleOnSearch, status]);
|
|
256
316
|
return jsx(IntlMessagesProvider, {
|
|
257
317
|
defaultMessages: i18nEN,
|
|
258
318
|
loaderFn: fetchMessagesForLocale
|
|
@@ -263,8 +323,9 @@ const PlainAssetsConfigModal = props => {
|
|
|
263
323
|
shouldScrollInViewport: true,
|
|
264
324
|
shouldCloseOnOverlayClick: false
|
|
265
325
|
}, jsx(ModalHeader, null, renderModalTitleContent()), jsx(ModalBody, null, jsx("div", {
|
|
266
|
-
css: modalBodyWrapperStyles
|
|
267
|
-
},
|
|
326
|
+
css: [modalBodyWrapperStyles, errorState && modalBodyErrorWrapperStyles]
|
|
327
|
+
}, errorState ? renderErrorState() : jsx(RenderAssetsContent, {
|
|
328
|
+
isFetchingInitialData: assetsClientLoading,
|
|
268
329
|
status: status,
|
|
269
330
|
responseItems: responseItems,
|
|
270
331
|
visibleColumnKeys: visibleColumnKeys,
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { css, jsx } from '@emotion/react';
|
|
2
4
|
import { AccessRequired } from '../../../common/error-state/access-required';
|
|
3
5
|
import { ModalLoadingError } from '../../../common/error-state/modal-loading-error';
|
|
4
6
|
import { NoResults } from '../../../common/error-state/no-results';
|
|
5
7
|
import { EmptyState, IssueLikeDataTableView } from '../../../issue-like-table';
|
|
6
8
|
import { InitialStateView } from './initial-state-view';
|
|
9
|
+
// This is to prevent y scrollbar when initially fetching data
|
|
10
|
+
const emptyStateOverrideStyles = css({
|
|
11
|
+
height: '420px',
|
|
12
|
+
overflow: 'hidden'
|
|
13
|
+
});
|
|
7
14
|
export const RenderAssetsContent = props => {
|
|
8
15
|
const {
|
|
9
16
|
status,
|
|
@@ -15,10 +22,11 @@ export const RenderAssetsContent = props => {
|
|
|
15
22
|
columns,
|
|
16
23
|
defaultVisibleColumnKeys,
|
|
17
24
|
onVisibleColumnKeysChange,
|
|
18
|
-
modalRenderInstanceId
|
|
25
|
+
modalRenderInstanceId,
|
|
26
|
+
isFetchingInitialData
|
|
19
27
|
} = props;
|
|
20
28
|
const resolvedWithNoResults = status === 'resolved' && !responseItems.length;
|
|
21
|
-
const issueLikeDataTableView = useMemo(() =>
|
|
29
|
+
const issueLikeDataTableView = useMemo(() => jsx(IssueLikeDataTableView, {
|
|
22
30
|
testId: "asset-datasource-table",
|
|
23
31
|
status: status,
|
|
24
32
|
columns: columns,
|
|
@@ -30,16 +38,23 @@ export const RenderAssetsContent = props => {
|
|
|
30
38
|
onVisibleColumnKeysChange: onVisibleColumnKeysChange,
|
|
31
39
|
parentContainerRenderInstanceId: modalRenderInstanceId
|
|
32
40
|
}), [columns, defaultVisibleColumnKeys, hasNextPage, loadDatasourceDetails, onNextPage, onVisibleColumnKeysChange, responseItems, status, visibleColumnKeys, modalRenderInstanceId]);
|
|
33
|
-
if (
|
|
34
|
-
|
|
41
|
+
if (isFetchingInitialData) {
|
|
42
|
+
// Placing this check first as it's a priority before all others
|
|
43
|
+
return jsx("div", {
|
|
44
|
+
css: emptyStateOverrideStyles
|
|
45
|
+
}, jsx(EmptyState, {
|
|
46
|
+
testId: "assets-aql-datasource-modal--loading-state"
|
|
47
|
+
}));
|
|
48
|
+
} else if (status === 'rejected') {
|
|
49
|
+
return jsx(ModalLoadingError, null);
|
|
35
50
|
} else if (status === 'unauthorized') {
|
|
36
|
-
return
|
|
51
|
+
return jsx(AccessRequired, null);
|
|
37
52
|
} else if (status === 'empty') {
|
|
38
|
-
return
|
|
53
|
+
return jsx(InitialStateView, null);
|
|
39
54
|
} else if (resolvedWithNoResults) {
|
|
40
|
-
return
|
|
55
|
+
return jsx(NoResults, null);
|
|
41
56
|
} else if (status === 'loading' && !columns.length) {
|
|
42
|
-
return
|
|
57
|
+
return jsx(EmptyState, {
|
|
43
58
|
testId: "assets-aql-datasource-modal--loading-state"
|
|
44
59
|
});
|
|
45
60
|
}
|
|
@@ -35,7 +35,7 @@ export const AssetsSearchContainer = props => {
|
|
|
35
35
|
}, ({
|
|
36
36
|
formProps
|
|
37
37
|
}) => {
|
|
38
|
-
var _initialSearchData$ob, _initialSearchData$aq;
|
|
38
|
+
var _initialSearchData$ob, _initialSearchData$ob2, _initialSearchData$aq;
|
|
39
39
|
return jsx(FormContainer, _extends({}, formProps, {
|
|
40
40
|
id: SEARCH_FORM_ID
|
|
41
41
|
}), jsx(FormRowContainer, {
|
|
@@ -43,6 +43,7 @@ export const AssetsSearchContainer = props => {
|
|
|
43
43
|
}, modalTitle, jsx(SchemaSelectContainer, null, jsx(AssetsObjectSchemaSelect, {
|
|
44
44
|
value: (_initialSearchData$ob = initialSearchData.objectSchema) !== null && _initialSearchData$ob !== void 0 ? _initialSearchData$ob : undefined,
|
|
45
45
|
workspaceId: workspaceId,
|
|
46
|
+
initialObjectSchemas: (_initialSearchData$ob2 = initialSearchData.objectSchemas) !== null && _initialSearchData$ob2 !== void 0 ? _initialSearchData$ob2 : undefined,
|
|
46
47
|
classNamePrefix: "assets-datasource-modal--object-schema-select"
|
|
47
48
|
}))), jsx(FormRowContainer, null, jsx(AqlSearchInput, {
|
|
48
49
|
value: (_initialSearchData$aq = initialSearchData.aql) !== null && _initialSearchData$aq !== void 0 ? _initialSearchData$aq : DEFAULT_AQL_QUERY,
|