@atlaskit/link-datasource 2.9.4 → 2.9.6

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 CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/link-datasource
2
2
 
3
+ ## 2.9.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`941edf62401ae`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/941edf62401ae) -
8
+ This version removes `platform.design-system-team.use-default-select-in-popup-select_46rmj`
9
+ feature flag. The `PopupSelect` component now uses the internal `Select` component ensure the
10
+ accessibility of options with group labels for assistive technologies.
11
+
12
+ ## 2.9.5
13
+
14
+ ### Patch Changes
15
+
16
+ - [#127170](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/127170)
17
+ [`48d23d3dfb9a1`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/48d23d3dfb9a1) -
18
+ We are testing action discovery for datasources behind a feature flag. If this fix is successful
19
+ it will be available in a later release.
20
+ - Updated dependencies
21
+
3
22
  ## 2.9.4
4
23
 
5
24
  ### Patch Changes
@@ -15,6 +15,7 @@ var _linkClientExtension = require("@atlaskit/link-client-extension");
15
15
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
16
16
  var _analytics = require("../analytics");
17
17
  var _state = require("../state");
18
+ var _actions = require("../state/actions");
18
19
  var _useErrorLogger2 = _interopRequireDefault(require("./useErrorLogger"));
19
20
  var useDatasourceTableState = exports.useDatasourceTableState = function useDatasourceTableState(_ref) {
20
21
  var datasourceId = _ref.datasourceId,
@@ -29,6 +30,8 @@ var useDatasourceTableState = exports.useDatasourceTableState = function useData
29
30
  captureError = _useErrorLogger.captureError;
30
31
  var _useDatasourceActions = (0, _state.useDatasourceActions)(),
31
32
  onAddItems = _useDatasourceActions.onAddItems;
33
+ var _useDiscoverActions = (0, _actions.useDiscoverActions)(),
34
+ discoverActions = _useDiscoverActions.discoverActions;
32
35
  var idFieldCount = 1;
33
36
  var keyFieldCount = 1;
34
37
  var _useState = (0, _react.useState)([]),
@@ -215,6 +218,7 @@ var useDatasourceTableState = exports.useDatasourceTableState = function useData
215
218
  _yield$getDatasourceD4,
216
219
  access,
217
220
  _destinationObjectTypes,
221
+ product,
218
222
  _extensionKey,
219
223
  auth,
220
224
  _providerName,
@@ -223,7 +227,9 @@ var useDatasourceTableState = exports.useDatasourceTableState = function useData
223
227
  nextPageCursor,
224
228
  _totalCount,
225
229
  schema,
230
+ integrationKey,
226
231
  newIds,
232
+ aris,
227
233
  isUserLoadingNextPage,
228
234
  currentLoadedItemCount,
229
235
  newlyLoadedItemCount,
@@ -265,6 +271,7 @@ var useDatasourceTableState = exports.useDatasourceTableState = function useData
265
271
  _yield$getDatasourceD4 = _yield$getDatasourceD3.meta;
266
272
  access = _yield$getDatasourceD4.access;
267
273
  _destinationObjectTypes = _yield$getDatasourceD4.destinationObjectTypes;
274
+ product = _yield$getDatasourceD4.product;
268
275
  _extensionKey = _yield$getDatasourceD4.extensionKey;
269
276
  auth = _yield$getDatasourceD4.auth;
270
277
  _providerName = _yield$getDatasourceD4.providerName;
@@ -274,21 +281,21 @@ var useDatasourceTableState = exports.useDatasourceTableState = function useData
274
281
  _totalCount = _yield$getDatasourceD5.totalCount;
275
282
  schema = _yield$getDatasourceD5.schema;
276
283
  if (!((_currentAbortControll = currentAbortController) !== null && _currentAbortControll !== void 0 && _currentAbortControll.signal.aborted)) {
277
- _context2.next = 27;
284
+ _context2.next = 28;
278
285
  break;
279
286
  }
280
287
  throw new Error('Aborted');
281
- case 27:
288
+ case 28:
282
289
  setExtensionKey(_extensionKey);
283
290
  setProviderName(_providerName);
284
291
  if (!(access === 'unauthorized' || access === 'forbidden')) {
285
- _context2.next = 33;
292
+ _context2.next = 34;
286
293
  break;
287
294
  }
288
295
  setStatus(access);
289
296
  setAuthDetails(auth || initialEmptyArray);
290
297
  return _context2.abrupt("return");
291
- case 33:
298
+ case 34:
292
299
  setDestinationObjectTypes(_destinationObjectTypes);
293
300
  setTotalCount(_totalCount);
294
301
  setNextCursor(nextPageCursor);
@@ -299,10 +306,29 @@ var useDatasourceTableState = exports.useDatasourceTableState = function useData
299
306
  return [].concat((0, _toConsumableArray2.default)(currentResponseItems), (0, _toConsumableArray2.default)(items));
300
307
  });
301
308
  if ((0, _platformFeatureFlags.fg)('enable_datasource_react_sweet_state')) {
302
- newIds = onAddItems(items);
309
+ /**
310
+ * Product is typed as any.
311
+ */
312
+ integrationKey = product;
313
+ newIds = onAddItems(items, typeof integrationKey === 'string' ? integrationKey : undefined);
303
314
  setResponseItemIds(function (currentIds) {
304
315
  return [].concat((0, _toConsumableArray2.default)(currentIds), (0, _toConsumableArray2.default)(newIds));
305
316
  });
317
+ if ((0, _platformFeatureFlags.fg)('platform-datasources-enable-two-way-sync')) {
318
+ if (typeof integrationKey === 'string') {
319
+ aris = items.reduce(function (acc, item) {
320
+ var _item$ari;
321
+ return typeof ((_item$ari = item.ari) === null || _item$ari === void 0 ? void 0 : _item$ari.data) === 'string' ? [].concat((0, _toConsumableArray2.default)(acc), [item.ari.data]) : acc;
322
+ }, []);
323
+ if (aris.length) {
324
+ discoverActions({
325
+ aris: aris,
326
+ integrationKey: integrationKey,
327
+ fieldKeys: fieldKeys
328
+ });
329
+ }
330
+ }
331
+ }
306
332
  }
307
333
  setHasNextPage(Boolean(nextPageCursor));
308
334
  if (fieldKeys.length > 0) {
@@ -322,39 +348,39 @@ var useDatasourceTableState = exports.useDatasourceTableState = function useData
322
348
  });
323
349
  }
324
350
  setStatus('resolved');
325
- _context2.next = 58;
351
+ _context2.next = 59;
326
352
  break;
327
- case 46:
328
- _context2.prev = 46;
353
+ case 47:
354
+ _context2.prev = 47;
329
355
  _context2.t0 = _context2["catch"](10);
330
356
  if (!(_context2.t0.message === 'Aborted')) {
331
- _context2.next = 50;
357
+ _context2.next = 51;
332
358
  break;
333
359
  }
334
360
  return _context2.abrupt("return");
335
- case 50:
361
+ case 51:
336
362
  captureError('onNextPage', _context2.t0);
337
363
  if (!(_context2.t0 instanceof Response && _context2.t0.status === 401)) {
338
- _context2.next = 54;
364
+ _context2.next = 55;
339
365
  break;
340
366
  }
341
367
  setStatus('unauthorized');
342
368
  return _context2.abrupt("return");
343
- case 54:
369
+ case 55:
344
370
  if (!(_context2.t0 instanceof Response && _context2.t0.status === 403)) {
345
- _context2.next = 57;
371
+ _context2.next = 58;
346
372
  break;
347
373
  }
348
374
  setStatus('forbidden');
349
375
  return _context2.abrupt("return");
350
- case 57:
351
- setStatus('rejected');
352
376
  case 58:
377
+ setStatus('rejected');
378
+ case 59:
353
379
  case "end":
354
380
  return _context2.stop();
355
381
  }
356
- }, _callee2, null, [[10, 46]]);
357
- })), [captureError, parameters, fieldKeys, nextCursor, responseItems, setResponseItemIds, onAddItems, getDatasourceData, datasourceId, applySchemaProperties, fireEvent, fullSchema, initialEmptyArray]);
382
+ }, _callee2, null, [[10, 47]]);
383
+ })), [captureError, parameters, fieldKeys, nextCursor, responseItems, setResponseItemIds, onAddItems, getDatasourceData, datasourceId, applySchemaProperties, fireEvent, fullSchema, initialEmptyArray, discoverActions]);
358
384
  var reset = (0, _react.useCallback)(function (options) {
359
385
  setResponseItems(initialEmptyArray);
360
386
  setResponseItemIds(initialEmptyArray);
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.useExecuteAtomicAction = exports.useDiscoverActions = exports.useAtomicUpdateActionSchema = exports.actions = exports.ActionsStore = void 0;
8
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
+ var _react = require("react");
13
+ var _reactSweetState = require("react-sweet-state");
14
+ var _linkClientExtension = require("@atlaskit/link-client-extension");
15
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
16
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
17
+ /**
18
+ * Atomic actions available for an integration (by field)
19
+ * @example
20
+ * ```ts
21
+ * {
22
+ * jira: {
23
+ * summary: {
24
+ * actionKey: 'atlassian:issue:update:summary',
25
+ * type: 'string',
26
+ * description: 'Update issue summary',
27
+ * }
28
+ * }
29
+ * }
30
+ * ```
31
+ */
32
+
33
+ /**
34
+ * Permissions available for a target
35
+ */
36
+
37
+ /**
38
+ * User permissions for actions on target (ARI) properties
39
+ * @example
40
+ * ```ts
41
+ * {
42
+ * 'ari:cloud:jira:63cecfe3-16fa-4ee1-8e8d-047cc4b18980:issue/1': {
43
+ * summary: {
44
+ * isEditable: true
45
+ * }
46
+ * }
47
+ * }
48
+ * ```
49
+ */
50
+
51
+ var getInitialState = function getInitialState() {
52
+ return {
53
+ actionsByIntegration: {},
54
+ permissions: {}
55
+ };
56
+ };
57
+ var actions = exports.actions = {
58
+ discoverActions: function discoverActions(api, request) {
59
+ return /*#__PURE__*/function () {
60
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
61
+ var setState, getState, response, _getState, currentActions, currentPermissions, actionsByIntegration, permissions;
62
+ return _regenerator.default.wrap(function _callee$(_context) {
63
+ while (1) switch (_context.prev = _context.next) {
64
+ case 0:
65
+ setState = _ref.setState, getState = _ref.getState;
66
+ _context.next = 3;
67
+ return api.getDatasourceActionsAndPermissions(request);
68
+ case 3:
69
+ response = _context.sent;
70
+ if ('permissions' in response) {
71
+ _getState = getState(), currentActions = _getState.actionsByIntegration, currentPermissions = _getState.permissions;
72
+ actionsByIntegration = response.actions.reduce(function (acc, action) {
73
+ return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2.default)({}, action.integrationKey, _objectSpread(_objectSpread({}, acc[action.integrationKey]), {}, (0, _defineProperty2.default)({}, action.fieldKey, {
74
+ actionKey: action.actionKey,
75
+ type: action.type
76
+ }))));
77
+ }, currentActions);
78
+ permissions = response.permissions.data.reduce(function (acc, permission) {
79
+ return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2.default)({}, permission.ari, _objectSpread(_objectSpread({}, acc[permission.ari]), {}, (0, _defineProperty2.default)({}, permission.fieldKey, {
80
+ isEditable: permission.isEditable
81
+ }))));
82
+ }, currentPermissions);
83
+ setState({
84
+ actionsByIntegration: actionsByIntegration,
85
+ permissions: permissions
86
+ });
87
+ }
88
+ case 5:
89
+ case "end":
90
+ return _context.stop();
91
+ }
92
+ }, _callee);
93
+ }));
94
+ return function (_x) {
95
+ return _ref2.apply(this, arguments);
96
+ };
97
+ }();
98
+ }
99
+ };
100
+ var ActionsStore = exports.ActionsStore = (0, _reactSweetState.createStore)({
101
+ name: 'actions-store',
102
+ initialState: getInitialState(),
103
+ actions: actions
104
+ });
105
+ var useActionStoreActions = (0, _reactSweetState.createActionsHook)(ActionsStore);
106
+ var useDiscoverActions = exports.useDiscoverActions = function useDiscoverActions() {
107
+ var _useDatasourceClientE = (0, _linkClientExtension.useDatasourceClientExtension)(),
108
+ getDatasourceActionsAndPermissions = _useDatasourceClientE.getDatasourceActionsAndPermissions;
109
+ var _useActionStoreAction = useActionStoreActions(),
110
+ discoverActions = _useActionStoreAction.discoverActions;
111
+ return {
112
+ discoverActions: (0, _react.useMemo)(function () {
113
+ return discoverActions.bind(null, {
114
+ getDatasourceActionsAndPermissions: getDatasourceActionsAndPermissions
115
+ });
116
+ }, [discoverActions, getDatasourceActionsAndPermissions])
117
+ };
118
+ };
119
+ var getFieldUpdateActionByAri = function getFieldUpdateActionByAri(state, _ref3) {
120
+ var _state$permissions$ar, _state$actionsByInteg;
121
+ var ari = _ref3.ari,
122
+ fieldKey = _ref3.fieldKey,
123
+ integrationKey = _ref3.integrationKey;
124
+ var isEditable = (_state$permissions$ar = state.permissions[ari]) === null || _state$permissions$ar === void 0 || (_state$permissions$ar = _state$permissions$ar[fieldKey]) === null || _state$permissions$ar === void 0 ? void 0 : _state$permissions$ar.isEditable;
125
+ if (isEditable === false) {
126
+ return;
127
+ }
128
+ return (_state$actionsByInteg = state.actionsByIntegration[integrationKey]) === null || _state$actionsByInteg === void 0 ? void 0 : _state$actionsByInteg[fieldKey];
129
+ };
130
+
131
+ /**
132
+ * Retrieves the action schema for a given ARI + fieldKey + integrationKey
133
+ */
134
+ var useAtomicUpdateActionSchema = exports.useAtomicUpdateActionSchema = (0, _reactSweetState.createHook)(ActionsStore, {
135
+ selector: getFieldUpdateActionByAri
136
+ });
137
+
138
+ /**
139
+ * Given an ARI + fieldKey + integrationKey
140
+ * Returns an executable action that updates a field on the entity if the user has permissions to do so
141
+ *
142
+ * @example
143
+ * ```tsx
144
+ * const { execute } = useExecuteAtomicAction({ ari, fieldKey: 'summary', integrationKey: 'jira' });
145
+ *
146
+ * return <button onClick={() => execute('New summary')}>Update summary</button>;
147
+ * ```
148
+ */
149
+ var useExecuteAtomicAction = exports.useExecuteAtomicAction = function useExecuteAtomicAction(_ref4) {
150
+ var ari = _ref4.ari,
151
+ fieldKey = _ref4.fieldKey,
152
+ integrationKey = _ref4.integrationKey;
153
+ var _useAtomicUpdateActio = useAtomicUpdateActionSchema({
154
+ ari: ari,
155
+ fieldKey: fieldKey,
156
+ integrationKey: integrationKey
157
+ }),
158
+ _useAtomicUpdateActio2 = (0, _slicedToArray2.default)(_useAtomicUpdateActio, 1),
159
+ schema = _useAtomicUpdateActio2[0];
160
+ var _useDatasourceClientE2 = (0, _linkClientExtension.useDatasourceClientExtension)(),
161
+ executeAction = _useDatasourceClientE2.executeAtomicAction;
162
+ var execute = (0, _react.useCallback)(function (value) {
163
+ if (!schema) {
164
+ throw new Error('No action schema found.');
165
+ }
166
+ executeAction({
167
+ integrationKey: integrationKey,
168
+ actionKey: schema.actionKey,
169
+ parameters: {
170
+ inputs: (0, _defineProperty2.default)({}, fieldKey, value),
171
+ target: {
172
+ ari: ari
173
+ }
174
+ }
175
+ });
176
+ }, [executeAction, integrationKey, schema, fieldKey, ari]);
177
+ if (!schema) {
178
+ return null;
179
+ }
180
+ return {
181
+ execute: execute
182
+ };
183
+ };
@@ -20,19 +20,22 @@ var getInitialState = function getInitialState() {
20
20
  };
21
21
  };
22
22
  var actions = exports.actions = {
23
- onAddItems: function onAddItems(items) {
23
+ onAddItems: function onAddItems(items, integrationKey) {
24
24
  return function (_ref) {
25
25
  var setState = _ref.setState,
26
26
  getState = _ref.getState;
27
27
  var oldItems = _objectSpread({}, getState().items);
28
28
  var _items$reduce = items.reduce(function (_ref2, item) {
29
- var _item$ari;
29
+ var _item$ari, _oldItems$id;
30
30
  var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
31
31
  ids = _ref3[0],
32
32
  itemMap = _ref3[1];
33
33
  var ari = typeof ((_item$ari = item['ari']) === null || _item$ari === void 0 ? void 0 : _item$ari.data) === 'string' ? item['ari'].data : undefined;
34
34
  var id = ari !== null && ari !== void 0 ? ari : (0, _uuid.v4)();
35
- return [[].concat((0, _toConsumableArray2.default)(ids), [id]), _objectSpread(_objectSpread({}, itemMap), {}, (0, _defineProperty2.default)({}, id, _objectSpread(_objectSpread({}, oldItems[id]), item)))];
35
+ return [[].concat((0, _toConsumableArray2.default)(ids), [id]), _objectSpread(_objectSpread({}, itemMap), {}, (0, _defineProperty2.default)({}, id, {
36
+ integrationKey: integrationKey,
37
+ data: _objectSpread(_objectSpread({}, (_oldItems$id = oldItems[id]) === null || _oldItems$id === void 0 ? void 0 : _oldItems$id.data), item)
38
+ }))];
36
39
  }, [[], oldItems]),
37
40
  _items$reduce2 = (0, _slicedToArray2.default)(_items$reduce, 2),
38
41
  newItemIds = _items$reduce2[0],
@@ -51,8 +54,9 @@ var Store = exports.Store = (0, _reactSweetState.createStore)({
51
54
  });
52
55
  var useDatasourceItem = exports.useDatasourceItem = (0, _reactSweetState.createStateHook)(Store, {
53
56
  selector: function selector(state, _ref4) {
57
+ var _state$items$id;
54
58
  var id = _ref4.id;
55
- return state.items[id];
59
+ return (_state$items$id = state.items[id]) === null || _state$items$id === void 0 ? void 0 : _state$items$id.data;
56
60
  }
57
61
  });
58
62
  var useDatasourceActions = exports.useDatasourceActions = (0, _reactSweetState.createActionsHook)(Store);
@@ -107,7 +107,7 @@ var ColumnPicker = exports.ColumnPicker = function ColumnPicker(_ref) {
107
107
  if (allOptions.length) {
108
108
  var _pickerRef$current;
109
109
  // necessary to refocus the search input after the loading state
110
- pickerRef === null || pickerRef === void 0 || (_pickerRef$current = pickerRef.current) === null || _pickerRef$current === void 0 || (_pickerRef$current = _pickerRef$current.selectRef) === null || _pickerRef$current === void 0 || (_pickerRef$current = _pickerRef$current.inputRef) === null || _pickerRef$current === void 0 || _pickerRef$current.focus();
110
+ pickerRef === null || pickerRef === void 0 || (_pickerRef$current = pickerRef.current) === null || _pickerRef$current === void 0 || (_pickerRef$current = _pickerRef$current.selectRef) === null || _pickerRef$current === void 0 || (_pickerRef$current = _pickerRef$current.select) === null || _pickerRef$current === void 0 || (_pickerRef$current = _pickerRef$current.inputRef) === null || _pickerRef$current === void 0 || _pickerRef$current.focus();
111
111
  }
112
112
  }, [allOptions]);
113
113
  (0, _react.useEffect)(function () {
@@ -4,6 +4,7 @@ import { DEFAULT_GET_DATASOURCE_DATA_PAGE_SIZE, useDatasourceClientExtension } f
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
5
5
  import { useDatasourceAnalyticsEvents } from '../analytics';
6
6
  import { useDatasourceActions } from '../state';
7
+ import { useDiscoverActions } from '../state/actions';
7
8
  import useErrorLogger from './useErrorLogger';
8
9
  export const useDatasourceTableState = ({
9
10
  datasourceId,
@@ -21,6 +22,9 @@ export const useDatasourceTableState = ({
21
22
  const {
22
23
  onAddItems
23
24
  } = useDatasourceActions();
25
+ const {
26
+ discoverActions
27
+ } = useDiscoverActions();
24
28
  const idFieldCount = 1;
25
29
  const keyFieldCount = 1;
26
30
  const [initialEmptyArray] = useState([]);
@@ -152,6 +156,7 @@ export const useDatasourceTableState = ({
152
156
  meta: {
153
157
  access,
154
158
  destinationObjectTypes,
159
+ product,
155
160
  extensionKey,
156
161
  auth,
157
162
  providerName
@@ -186,8 +191,27 @@ export const useDatasourceTableState = ({
186
191
  return [...currentResponseItems, ...items];
187
192
  });
188
193
  if (fg('enable_datasource_react_sweet_state')) {
189
- const newIds = onAddItems(items);
194
+ /**
195
+ * Product is typed as any.
196
+ */
197
+ const integrationKey = product;
198
+ const newIds = onAddItems(items, typeof integrationKey === 'string' ? integrationKey : undefined);
190
199
  setResponseItemIds(currentIds => [...currentIds, ...newIds]);
200
+ if (fg('platform-datasources-enable-two-way-sync')) {
201
+ if (typeof integrationKey === 'string') {
202
+ const aris = items.reduce((acc, item) => {
203
+ var _item$ari;
204
+ return typeof ((_item$ari = item.ari) === null || _item$ari === void 0 ? void 0 : _item$ari.data) === 'string' ? [...acc, item.ari.data] : acc;
205
+ }, []);
206
+ if (aris.length) {
207
+ discoverActions({
208
+ aris,
209
+ integrationKey,
210
+ fieldKeys
211
+ });
212
+ }
213
+ }
214
+ }
191
215
  }
192
216
  setHasNextPage(Boolean(nextPageCursor));
193
217
  if (fieldKeys.length > 0) {
@@ -228,7 +252,7 @@ export const useDatasourceTableState = ({
228
252
  }
229
253
  setStatus('rejected');
230
254
  }
231
- }, [captureError, parameters, fieldKeys, nextCursor, responseItems, setResponseItemIds, onAddItems, getDatasourceData, datasourceId, applySchemaProperties, fireEvent, fullSchema, initialEmptyArray]);
255
+ }, [captureError, parameters, fieldKeys, nextCursor, responseItems, setResponseItemIds, onAddItems, getDatasourceData, datasourceId, applySchemaProperties, fireEvent, fullSchema, initialEmptyArray, discoverActions]);
232
256
  const reset = useCallback(options => {
233
257
  setResponseItems(initialEmptyArray);
234
258
  setResponseItemIds(initialEmptyArray);
@@ -0,0 +1,166 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { createActionsHook, createHook, createStore } from 'react-sweet-state';
3
+ import { useDatasourceClientExtension } from '@atlaskit/link-client-extension';
4
+
5
+ /**
6
+ * Atomic actions available for an integration (by field)
7
+ * @example
8
+ * ```ts
9
+ * {
10
+ * jira: {
11
+ * summary: {
12
+ * actionKey: 'atlassian:issue:update:summary',
13
+ * type: 'string',
14
+ * description: 'Update issue summary',
15
+ * }
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+
21
+ /**
22
+ * Permissions available for a target
23
+ */
24
+
25
+ /**
26
+ * User permissions for actions on target (ARI) properties
27
+ * @example
28
+ * ```ts
29
+ * {
30
+ * 'ari:cloud:jira:63cecfe3-16fa-4ee1-8e8d-047cc4b18980:issue/1': {
31
+ * summary: {
32
+ * isEditable: true
33
+ * }
34
+ * }
35
+ * }
36
+ * ```
37
+ */
38
+
39
+ const getInitialState = () => ({
40
+ actionsByIntegration: {},
41
+ permissions: {}
42
+ });
43
+ export const actions = {
44
+ discoverActions: (api, request) => async ({
45
+ setState,
46
+ getState
47
+ }) => {
48
+ const response = await api.getDatasourceActionsAndPermissions(request);
49
+ if ('permissions' in response) {
50
+ const {
51
+ actionsByIntegration: currentActions,
52
+ permissions: currentPermissions
53
+ } = getState();
54
+ const actionsByIntegration = response.actions.reduce((acc, action) => ({
55
+ ...acc,
56
+ [action.integrationKey]: {
57
+ ...acc[action.integrationKey],
58
+ [action.fieldKey]: {
59
+ actionKey: action.actionKey,
60
+ type: action.type
61
+ }
62
+ }
63
+ }), currentActions);
64
+ const permissions = response.permissions.data.reduce((acc, permission) => ({
65
+ ...acc,
66
+ [permission.ari]: {
67
+ ...acc[permission.ari],
68
+ [permission.fieldKey]: {
69
+ isEditable: permission.isEditable
70
+ }
71
+ }
72
+ }), currentPermissions);
73
+ setState({
74
+ actionsByIntegration,
75
+ permissions
76
+ });
77
+ }
78
+ }
79
+ };
80
+ export const ActionsStore = createStore({
81
+ name: 'actions-store',
82
+ initialState: getInitialState(),
83
+ actions
84
+ });
85
+ const useActionStoreActions = createActionsHook(ActionsStore);
86
+ export const useDiscoverActions = () => {
87
+ const {
88
+ getDatasourceActionsAndPermissions
89
+ } = useDatasourceClientExtension();
90
+ const {
91
+ discoverActions
92
+ } = useActionStoreActions();
93
+ return {
94
+ discoverActions: useMemo(() => discoverActions.bind(null, {
95
+ getDatasourceActionsAndPermissions
96
+ }), [discoverActions, getDatasourceActionsAndPermissions])
97
+ };
98
+ };
99
+ const getFieldUpdateActionByAri = (state, {
100
+ ari,
101
+ fieldKey,
102
+ integrationKey
103
+ }) => {
104
+ var _state$permissions$ar, _state$permissions$ar2, _state$actionsByInteg;
105
+ const isEditable = (_state$permissions$ar = state.permissions[ari]) === null || _state$permissions$ar === void 0 ? void 0 : (_state$permissions$ar2 = _state$permissions$ar[fieldKey]) === null || _state$permissions$ar2 === void 0 ? void 0 : _state$permissions$ar2.isEditable;
106
+ if (isEditable === false) {
107
+ return;
108
+ }
109
+ return (_state$actionsByInteg = state.actionsByIntegration[integrationKey]) === null || _state$actionsByInteg === void 0 ? void 0 : _state$actionsByInteg[fieldKey];
110
+ };
111
+
112
+ /**
113
+ * Retrieves the action schema for a given ARI + fieldKey + integrationKey
114
+ */
115
+ export const useAtomicUpdateActionSchema = createHook(ActionsStore, {
116
+ selector: getFieldUpdateActionByAri
117
+ });
118
+
119
+ /**
120
+ * Given an ARI + fieldKey + integrationKey
121
+ * Returns an executable action that updates a field on the entity if the user has permissions to do so
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * const { execute } = useExecuteAtomicAction({ ari, fieldKey: 'summary', integrationKey: 'jira' });
126
+ *
127
+ * return <button onClick={() => execute('New summary')}>Update summary</button>;
128
+ * ```
129
+ */
130
+ export const useExecuteAtomicAction = ({
131
+ ari,
132
+ fieldKey,
133
+ integrationKey
134
+ }) => {
135
+ const [schema] = useAtomicUpdateActionSchema({
136
+ ari,
137
+ fieldKey,
138
+ integrationKey
139
+ });
140
+ const {
141
+ executeAtomicAction: executeAction
142
+ } = useDatasourceClientExtension();
143
+ const execute = useCallback(value => {
144
+ if (!schema) {
145
+ throw new Error('No action schema found.');
146
+ }
147
+ executeAction({
148
+ integrationKey,
149
+ actionKey: schema.actionKey,
150
+ parameters: {
151
+ inputs: {
152
+ [fieldKey]: value
153
+ },
154
+ target: {
155
+ ari
156
+ }
157
+ }
158
+ });
159
+ }, [executeAction, integrationKey, schema, fieldKey, ari]);
160
+ if (!schema) {
161
+ return null;
162
+ }
163
+ return {
164
+ execute
165
+ };
166
+ };
@@ -6,7 +6,7 @@ const getInitialState = () => ({
6
6
  items: {}
7
7
  });
8
8
  export const actions = {
9
- onAddItems: items => ({
9
+ onAddItems: (items, integrationKey) => ({
10
10
  setState,
11
11
  getState
12
12
  }) => {
@@ -14,14 +14,17 @@ export const actions = {
14
14
  ...getState().items
15
15
  };
16
16
  const [newItemIds, newItems] = items.reduce(([ids, itemMap], item) => {
17
- var _item$ari;
17
+ var _item$ari, _oldItems$id;
18
18
  const ari = typeof ((_item$ari = item['ari']) === null || _item$ari === void 0 ? void 0 : _item$ari.data) === 'string' ? item['ari'].data : undefined;
19
19
  const id = ari !== null && ari !== void 0 ? ari : uuidv4();
20
20
  return [[...ids, id], {
21
21
  ...itemMap,
22
22
  [id]: {
23
- ...oldItems[id],
24
- ...item
23
+ integrationKey,
24
+ data: {
25
+ ...((_oldItems$id = oldItems[id]) === null || _oldItems$id === void 0 ? void 0 : _oldItems$id.data),
26
+ ...item
27
+ }
25
28
  }
26
29
  }];
27
30
  }, [[], oldItems]);
@@ -39,7 +42,10 @@ export const Store = createStore({
39
42
  export const useDatasourceItem = createStateHook(Store, {
40
43
  selector: (state, {
41
44
  id
42
- }) => state.items[id]
45
+ }) => {
46
+ var _state$items$id;
47
+ return (_state$items$id = state.items[id]) === null || _state$items$id === void 0 ? void 0 : _state$items$id.data;
48
+ }
43
49
  });
44
50
  export const useDatasourceActions = createActionsHook(Store);
45
51
  const Container = createContainer(Store);