@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/analytics/constants.js +1 -1
  3. package/dist/cjs/hooks/useAssetsClient.js +71 -27
  4. package/dist/cjs/services/cmdbService.js +128 -28
  5. package/dist/cjs/services/cmdbService.utils.js +64 -0
  6. package/dist/cjs/ui/assets-modal/modal/index.js +73 -9
  7. package/dist/cjs/ui/assets-modal/modal/render-assets-content/index.js +25 -12
  8. package/dist/cjs/ui/assets-modal/search-container/index.js +2 -1
  9. package/dist/cjs/ui/assets-modal/search-container/object-schema-select/index.js +21 -63
  10. package/dist/cjs/ui/issue-like-table/render-type/date-time/index.js +7 -1
  11. package/dist/es2019/analytics/constants.js +1 -1
  12. package/dist/es2019/hooks/useAssetsClient.js +39 -15
  13. package/dist/es2019/services/cmdbService.js +60 -14
  14. package/dist/es2019/services/cmdbService.utils.js +39 -0
  15. package/dist/es2019/ui/assets-modal/modal/index.js +70 -9
  16. package/dist/es2019/ui/assets-modal/modal/render-assets-content/index.js +24 -9
  17. package/dist/es2019/ui/assets-modal/search-container/index.js +2 -1
  18. package/dist/es2019/ui/assets-modal/search-container/object-schema-select/index.js +2 -25
  19. package/dist/es2019/ui/issue-like-table/render-type/date-time/index.js +7 -1
  20. package/dist/esm/analytics/constants.js +1 -1
  21. package/dist/esm/hooks/useAssetsClient.js +72 -28
  22. package/dist/esm/services/cmdbService.js +128 -28
  23. package/dist/esm/services/cmdbService.utils.js +57 -0
  24. package/dist/esm/ui/assets-modal/modal/index.js +73 -9
  25. package/dist/esm/ui/assets-modal/modal/render-assets-content/index.js +24 -9
  26. package/dist/esm/ui/assets-modal/search-container/index.js +2 -1
  27. package/dist/esm/ui/assets-modal/search-container/object-schema-select/index.js +18 -60
  28. package/dist/esm/ui/issue-like-table/render-type/date-time/index.js +7 -1
  29. package/dist/types/hooks/useAssetsClient.d.ts +5 -1
  30. package/dist/types/services/cmdbService.utils.d.ts +9 -0
  31. package/dist/types/ui/assets-modal/modal/render-assets-content/index.d.ts +3 -2
  32. package/dist/types/ui/assets-modal/search-container/index.d.ts +1 -0
  33. package/dist/types/ui/assets-modal/search-container/object-schema-select/index.d.ts +2 -1
  34. package/dist/types-ts4.5/hooks/useAssetsClient.d.ts +5 -1
  35. package/dist/types-ts4.5/services/cmdbService.utils.d.ts +9 -0
  36. package/dist/types-ts4.5/ui/assets-modal/modal/render-assets-content/index.d.ts +3 -2
  37. package/dist/types-ts4.5/ui/assets-modal/search-container/index.d.ts +1 -0
  38. package/dist/types-ts4.5/ui/assets-modal/search-container/object-schema-select/index.d.ts +2 -1
  39. 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 _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(inputValue) {
104
- var _yield$fetchObjectSch2, objectSchemas;
105
- return _regenerator.default.wrap(function _callee2$(_context2) {
106
- while (1) switch (_context2.prev = _context2.next) {
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
- _context2.next = 2;
66
+ _context.next = 2;
109
67
  return fetchObjectSchemas(inputValue);
110
68
  case 2:
111
- _yield$fetchObjectSch2 = _context2.sent;
112
- objectSchemas = _yield$fetchObjectSch2.objectSchemas;
113
- return _context2.abrupt("return", mapObjectSchemasToOptions(objectSchemas));
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 _context2.stop();
74
+ return _context.stop();
117
75
  }
118
- }, _callee2);
76
+ }, _callee);
119
77
  }));
120
78
  return function loadOptions(_x) {
121
- return _ref3.apply(this, arguments);
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, _react2.jsx)(_styled.FieldContainer, null, (0, _react2.jsx)(_form.Field, {
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 (_ref4) {
138
- var _ref4$fieldProps = _ref4.fieldProps,
139
- _onChange = _ref4$fieldProps.onChange,
140
- onFocus = _ref4$fieldProps.onFocus,
141
- restFieldProps = (0, _objectWithoutProperties2.default)(_ref4$fieldProps, _excluded);
142
- return (0, _react2.jsx)(_select.AsyncSelect, (0, _extends2.default)({
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: defaultOptions !== null && defaultOptions !== void 0 ? 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,5 +1,5 @@
1
1
  export const EVENT_CHANNEL = 'media';
2
2
  export const packageMetaData = {
3
3
  packageName: "@atlaskit/link-datasource",
4
- packageVersion: "1.15.2"
4
+ packageVersion: "1.15.4"
5
5
  };
@@ -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 [objectSchema, setObjectSchema] = useState();
7
- const [error, setError] = useState();
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
- setError(undefined);
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
- setObjectSchema(fetchedObjectSchema);
20
- } catch {
21
- // Could update this to check if status is 404 and set objectSchemaError
36
+ setExistingObjectSchema(fetchedObjectSchema);
37
+ } catch (fetchObjectSchemaError) {
38
+ handleAssetsClientErrors(setExistingObjectSchemaError, fetchObjectSchemaError);
22
39
  }
23
40
  }
24
- } catch (err) {
25
- if (err instanceof Error) {
26
- setError(err);
27
- } else {
28
- setError(new Error('Unexpected error occured'));
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: error,
38
- objectSchema,
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
- const workspaceDetailsResponse = await request('get', url, undefined, undefined, [200, 201, 202, 203, 204]);
6
- if (!((_workspaceDetailsResp = workspaceDetailsResponse.results) !== null && _workspaceDetailsResp !== void 0 && _workspaceDetailsResp.length)) {
7
- throw new Error('No workspace results found');
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
- return request('post', url, {
14
- qlQuery: data.qlQuery,
15
- context: 'SMART_LINKS'
16
- }, undefined, [200, 201, 202, 203, 204]);
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
- return request('get', url, undefined, undefined, [200, 201, 202, 203, 204]);
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
- return request('get', url, undefined, undefined, [200, 201, 202, 203, 204]);
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
- objectSchema,
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 = !!workspaceError || status !== 'resolved' || assetsClientLoading || !aql || !schemaId;
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 (workspaceError) {
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
- }, [aql, assetsClientLoading, handleOnSearch, objectSchema, status, workspaceError, workspaceId]);
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
- }, workspaceError ? jsx(ModalLoadingError, null) : jsx(RenderAssetsContent, {
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
- import React, { useMemo } from 'react';
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(() => /*#__PURE__*/React.createElement(IssueLikeDataTableView, {
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 (status === 'rejected') {
34
- return /*#__PURE__*/React.createElement(ModalLoadingError, null);
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 /*#__PURE__*/React.createElement(AccessRequired, null);
51
+ return jsx(AccessRequired, null);
37
52
  } else if (status === 'empty') {
38
- return /*#__PURE__*/React.createElement(InitialStateView, null);
53
+ return jsx(InitialStateView, null);
39
54
  } else if (resolvedWithNoResults) {
40
- return /*#__PURE__*/React.createElement(NoResults, null);
55
+ return jsx(NoResults, null);
41
56
  } else if (status === 'loading' && !columns.length) {
42
- return /*#__PURE__*/React.createElement(EmptyState, {
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,