@atlaskit/link-datasource 2.9.4 → 2.9.5

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,15 @@
1
1
  # @atlaskit/link-datasource
2
2
 
3
+ ## 2.9.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#127170](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/127170)
8
+ [`48d23d3dfb9a1`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/48d23d3dfb9a1) -
9
+ We are testing action discovery for datasources behind a feature flag. If this fix is successful
10
+ it will be available in a later release.
11
+ - Updated dependencies
12
+
3
13
  ## 2.9.4
4
14
 
5
15
  ### 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);
@@ -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);
@@ -8,6 +8,7 @@ import { DEFAULT_GET_DATASOURCE_DATA_PAGE_SIZE, useDatasourceClientExtension } f
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
9
  import { useDatasourceAnalyticsEvents } from '../analytics';
10
10
  import { useDatasourceActions } from '../state';
11
+ import { useDiscoverActions } from '../state/actions';
11
12
  import useErrorLogger from './useErrorLogger';
12
13
  export var useDatasourceTableState = function useDatasourceTableState(_ref) {
13
14
  var datasourceId = _ref.datasourceId,
@@ -22,6 +23,8 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
22
23
  captureError = _useErrorLogger.captureError;
23
24
  var _useDatasourceActions = useDatasourceActions(),
24
25
  onAddItems = _useDatasourceActions.onAddItems;
26
+ var _useDiscoverActions = useDiscoverActions(),
27
+ discoverActions = _useDiscoverActions.discoverActions;
25
28
  var idFieldCount = 1;
26
29
  var keyFieldCount = 1;
27
30
  var _useState = useState([]),
@@ -208,6 +211,7 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
208
211
  _yield$getDatasourceD4,
209
212
  access,
210
213
  _destinationObjectTypes,
214
+ product,
211
215
  _extensionKey,
212
216
  auth,
213
217
  _providerName,
@@ -216,7 +220,9 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
216
220
  nextPageCursor,
217
221
  _totalCount,
218
222
  schema,
223
+ integrationKey,
219
224
  newIds,
225
+ aris,
220
226
  isUserLoadingNextPage,
221
227
  currentLoadedItemCount,
222
228
  newlyLoadedItemCount,
@@ -258,6 +264,7 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
258
264
  _yield$getDatasourceD4 = _yield$getDatasourceD3.meta;
259
265
  access = _yield$getDatasourceD4.access;
260
266
  _destinationObjectTypes = _yield$getDatasourceD4.destinationObjectTypes;
267
+ product = _yield$getDatasourceD4.product;
261
268
  _extensionKey = _yield$getDatasourceD4.extensionKey;
262
269
  auth = _yield$getDatasourceD4.auth;
263
270
  _providerName = _yield$getDatasourceD4.providerName;
@@ -267,21 +274,21 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
267
274
  _totalCount = _yield$getDatasourceD5.totalCount;
268
275
  schema = _yield$getDatasourceD5.schema;
269
276
  if (!((_currentAbortControll = currentAbortController) !== null && _currentAbortControll !== void 0 && _currentAbortControll.signal.aborted)) {
270
- _context2.next = 27;
277
+ _context2.next = 28;
271
278
  break;
272
279
  }
273
280
  throw new Error('Aborted');
274
- case 27:
281
+ case 28:
275
282
  setExtensionKey(_extensionKey);
276
283
  setProviderName(_providerName);
277
284
  if (!(access === 'unauthorized' || access === 'forbidden')) {
278
- _context2.next = 33;
285
+ _context2.next = 34;
279
286
  break;
280
287
  }
281
288
  setStatus(access);
282
289
  setAuthDetails(auth || initialEmptyArray);
283
290
  return _context2.abrupt("return");
284
- case 33:
291
+ case 34:
285
292
  setDestinationObjectTypes(_destinationObjectTypes);
286
293
  setTotalCount(_totalCount);
287
294
  setNextCursor(nextPageCursor);
@@ -292,10 +299,29 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
292
299
  return [].concat(_toConsumableArray(currentResponseItems), _toConsumableArray(items));
293
300
  });
294
301
  if (fg('enable_datasource_react_sweet_state')) {
295
- newIds = onAddItems(items);
302
+ /**
303
+ * Product is typed as any.
304
+ */
305
+ integrationKey = product;
306
+ newIds = onAddItems(items, typeof integrationKey === 'string' ? integrationKey : undefined);
296
307
  setResponseItemIds(function (currentIds) {
297
308
  return [].concat(_toConsumableArray(currentIds), _toConsumableArray(newIds));
298
309
  });
310
+ if (fg('platform-datasources-enable-two-way-sync')) {
311
+ if (typeof integrationKey === 'string') {
312
+ aris = items.reduce(function (acc, item) {
313
+ var _item$ari;
314
+ return typeof ((_item$ari = item.ari) === null || _item$ari === void 0 ? void 0 : _item$ari.data) === 'string' ? [].concat(_toConsumableArray(acc), [item.ari.data]) : acc;
315
+ }, []);
316
+ if (aris.length) {
317
+ discoverActions({
318
+ aris: aris,
319
+ integrationKey: integrationKey,
320
+ fieldKeys: fieldKeys
321
+ });
322
+ }
323
+ }
324
+ }
299
325
  }
300
326
  setHasNextPage(Boolean(nextPageCursor));
301
327
  if (fieldKeys.length > 0) {
@@ -315,39 +341,39 @@ export var useDatasourceTableState = function useDatasourceTableState(_ref) {
315
341
  });
316
342
  }
317
343
  setStatus('resolved');
318
- _context2.next = 58;
344
+ _context2.next = 59;
319
345
  break;
320
- case 46:
321
- _context2.prev = 46;
346
+ case 47:
347
+ _context2.prev = 47;
322
348
  _context2.t0 = _context2["catch"](10);
323
349
  if (!(_context2.t0.message === 'Aborted')) {
324
- _context2.next = 50;
350
+ _context2.next = 51;
325
351
  break;
326
352
  }
327
353
  return _context2.abrupt("return");
328
- case 50:
354
+ case 51:
329
355
  captureError('onNextPage', _context2.t0);
330
356
  if (!(_context2.t0 instanceof Response && _context2.t0.status === 401)) {
331
- _context2.next = 54;
357
+ _context2.next = 55;
332
358
  break;
333
359
  }
334
360
  setStatus('unauthorized');
335
361
  return _context2.abrupt("return");
336
- case 54:
362
+ case 55:
337
363
  if (!(_context2.t0 instanceof Response && _context2.t0.status === 403)) {
338
- _context2.next = 57;
364
+ _context2.next = 58;
339
365
  break;
340
366
  }
341
367
  setStatus('forbidden');
342
368
  return _context2.abrupt("return");
343
- case 57:
344
- setStatus('rejected');
345
369
  case 58:
370
+ setStatus('rejected');
371
+ case 59:
346
372
  case "end":
347
373
  return _context2.stop();
348
374
  }
349
- }, _callee2, null, [[10, 46]]);
350
- })), [captureError, parameters, fieldKeys, nextCursor, responseItems, setResponseItemIds, onAddItems, getDatasourceData, datasourceId, applySchemaProperties, fireEvent, fullSchema, initialEmptyArray]);
375
+ }, _callee2, null, [[10, 47]]);
376
+ })), [captureError, parameters, fieldKeys, nextCursor, responseItems, setResponseItemIds, onAddItems, getDatasourceData, datasourceId, applySchemaProperties, fireEvent, fullSchema, initialEmptyArray, discoverActions]);
351
377
  var reset = useCallback(function (options) {
352
378
  setResponseItems(initialEmptyArray);
353
379
  setResponseItemIds(initialEmptyArray);
@@ -0,0 +1,177 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
4
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
5
+ 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; }
6
+ 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) { _defineProperty(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; }
7
+ import { useCallback, useMemo } from 'react';
8
+ import { createActionsHook, createHook, createStore } from 'react-sweet-state';
9
+ import { useDatasourceClientExtension } from '@atlaskit/link-client-extension';
10
+
11
+ /**
12
+ * Atomic actions available for an integration (by field)
13
+ * @example
14
+ * ```ts
15
+ * {
16
+ * jira: {
17
+ * summary: {
18
+ * actionKey: 'atlassian:issue:update:summary',
19
+ * type: 'string',
20
+ * description: 'Update issue summary',
21
+ * }
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+
27
+ /**
28
+ * Permissions available for a target
29
+ */
30
+
31
+ /**
32
+ * User permissions for actions on target (ARI) properties
33
+ * @example
34
+ * ```ts
35
+ * {
36
+ * 'ari:cloud:jira:63cecfe3-16fa-4ee1-8e8d-047cc4b18980:issue/1': {
37
+ * summary: {
38
+ * isEditable: true
39
+ * }
40
+ * }
41
+ * }
42
+ * ```
43
+ */
44
+
45
+ var getInitialState = function getInitialState() {
46
+ return {
47
+ actionsByIntegration: {},
48
+ permissions: {}
49
+ };
50
+ };
51
+ export var actions = {
52
+ discoverActions: function discoverActions(api, request) {
53
+ return /*#__PURE__*/function () {
54
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
55
+ var setState, getState, response, _getState, currentActions, currentPermissions, actionsByIntegration, permissions;
56
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
57
+ while (1) switch (_context.prev = _context.next) {
58
+ case 0:
59
+ setState = _ref.setState, getState = _ref.getState;
60
+ _context.next = 3;
61
+ return api.getDatasourceActionsAndPermissions(request);
62
+ case 3:
63
+ response = _context.sent;
64
+ if ('permissions' in response) {
65
+ _getState = getState(), currentActions = _getState.actionsByIntegration, currentPermissions = _getState.permissions;
66
+ actionsByIntegration = response.actions.reduce(function (acc, action) {
67
+ return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, action.integrationKey, _objectSpread(_objectSpread({}, acc[action.integrationKey]), {}, _defineProperty({}, action.fieldKey, {
68
+ actionKey: action.actionKey,
69
+ type: action.type
70
+ }))));
71
+ }, currentActions);
72
+ permissions = response.permissions.data.reduce(function (acc, permission) {
73
+ return _objectSpread(_objectSpread({}, acc), {}, _defineProperty({}, permission.ari, _objectSpread(_objectSpread({}, acc[permission.ari]), {}, _defineProperty({}, permission.fieldKey, {
74
+ isEditable: permission.isEditable
75
+ }))));
76
+ }, currentPermissions);
77
+ setState({
78
+ actionsByIntegration: actionsByIntegration,
79
+ permissions: permissions
80
+ });
81
+ }
82
+ case 5:
83
+ case "end":
84
+ return _context.stop();
85
+ }
86
+ }, _callee);
87
+ }));
88
+ return function (_x) {
89
+ return _ref2.apply(this, arguments);
90
+ };
91
+ }();
92
+ }
93
+ };
94
+ export var ActionsStore = createStore({
95
+ name: 'actions-store',
96
+ initialState: getInitialState(),
97
+ actions: actions
98
+ });
99
+ var useActionStoreActions = createActionsHook(ActionsStore);
100
+ export var useDiscoverActions = function useDiscoverActions() {
101
+ var _useDatasourceClientE = useDatasourceClientExtension(),
102
+ getDatasourceActionsAndPermissions = _useDatasourceClientE.getDatasourceActionsAndPermissions;
103
+ var _useActionStoreAction = useActionStoreActions(),
104
+ discoverActions = _useActionStoreAction.discoverActions;
105
+ return {
106
+ discoverActions: useMemo(function () {
107
+ return discoverActions.bind(null, {
108
+ getDatasourceActionsAndPermissions: getDatasourceActionsAndPermissions
109
+ });
110
+ }, [discoverActions, getDatasourceActionsAndPermissions])
111
+ };
112
+ };
113
+ var getFieldUpdateActionByAri = function getFieldUpdateActionByAri(state, _ref3) {
114
+ var _state$permissions$ar, _state$actionsByInteg;
115
+ var ari = _ref3.ari,
116
+ fieldKey = _ref3.fieldKey,
117
+ integrationKey = _ref3.integrationKey;
118
+ 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;
119
+ if (isEditable === false) {
120
+ return;
121
+ }
122
+ return (_state$actionsByInteg = state.actionsByIntegration[integrationKey]) === null || _state$actionsByInteg === void 0 ? void 0 : _state$actionsByInteg[fieldKey];
123
+ };
124
+
125
+ /**
126
+ * Retrieves the action schema for a given ARI + fieldKey + integrationKey
127
+ */
128
+ export var useAtomicUpdateActionSchema = createHook(ActionsStore, {
129
+ selector: getFieldUpdateActionByAri
130
+ });
131
+
132
+ /**
133
+ * Given an ARI + fieldKey + integrationKey
134
+ * Returns an executable action that updates a field on the entity if the user has permissions to do so
135
+ *
136
+ * @example
137
+ * ```tsx
138
+ * const { execute } = useExecuteAtomicAction({ ari, fieldKey: 'summary', integrationKey: 'jira' });
139
+ *
140
+ * return <button onClick={() => execute('New summary')}>Update summary</button>;
141
+ * ```
142
+ */
143
+ export var useExecuteAtomicAction = function useExecuteAtomicAction(_ref4) {
144
+ var ari = _ref4.ari,
145
+ fieldKey = _ref4.fieldKey,
146
+ integrationKey = _ref4.integrationKey;
147
+ var _useAtomicUpdateActio = useAtomicUpdateActionSchema({
148
+ ari: ari,
149
+ fieldKey: fieldKey,
150
+ integrationKey: integrationKey
151
+ }),
152
+ _useAtomicUpdateActio2 = _slicedToArray(_useAtomicUpdateActio, 1),
153
+ schema = _useAtomicUpdateActio2[0];
154
+ var _useDatasourceClientE2 = useDatasourceClientExtension(),
155
+ executeAction = _useDatasourceClientE2.executeAtomicAction;
156
+ var execute = useCallback(function (value) {
157
+ if (!schema) {
158
+ throw new Error('No action schema found.');
159
+ }
160
+ executeAction({
161
+ integrationKey: integrationKey,
162
+ actionKey: schema.actionKey,
163
+ parameters: {
164
+ inputs: _defineProperty({}, fieldKey, value),
165
+ target: {
166
+ ari: ari
167
+ }
168
+ }
169
+ });
170
+ }, [executeAction, integrationKey, schema, fieldKey, ari]);
171
+ if (!schema) {
172
+ return null;
173
+ }
174
+ return {
175
+ execute: execute
176
+ };
177
+ };
@@ -13,19 +13,22 @@ var getInitialState = function getInitialState() {
13
13
  };
14
14
  };
15
15
  export var actions = {
16
- onAddItems: function onAddItems(items) {
16
+ onAddItems: function onAddItems(items, integrationKey) {
17
17
  return function (_ref) {
18
18
  var setState = _ref.setState,
19
19
  getState = _ref.getState;
20
20
  var oldItems = _objectSpread({}, getState().items);
21
21
  var _items$reduce = items.reduce(function (_ref2, item) {
22
- var _item$ari;
22
+ var _item$ari, _oldItems$id;
23
23
  var _ref3 = _slicedToArray(_ref2, 2),
24
24
  ids = _ref3[0],
25
25
  itemMap = _ref3[1];
26
26
  var ari = typeof ((_item$ari = item['ari']) === null || _item$ari === void 0 ? void 0 : _item$ari.data) === 'string' ? item['ari'].data : undefined;
27
27
  var id = ari !== null && ari !== void 0 ? ari : uuidv4();
28
- return [[].concat(_toConsumableArray(ids), [id]), _objectSpread(_objectSpread({}, itemMap), {}, _defineProperty({}, id, _objectSpread(_objectSpread({}, oldItems[id]), item)))];
28
+ return [[].concat(_toConsumableArray(ids), [id]), _objectSpread(_objectSpread({}, itemMap), {}, _defineProperty({}, id, {
29
+ integrationKey: integrationKey,
30
+ data: _objectSpread(_objectSpread({}, (_oldItems$id = oldItems[id]) === null || _oldItems$id === void 0 ? void 0 : _oldItems$id.data), item)
31
+ }))];
29
32
  }, [[], oldItems]),
30
33
  _items$reduce2 = _slicedToArray(_items$reduce, 2),
31
34
  newItemIds = _items$reduce2[0],
@@ -44,8 +47,9 @@ export var Store = createStore({
44
47
  });
45
48
  export var useDatasourceItem = createStateHook(Store, {
46
49
  selector: function selector(state, _ref4) {
50
+ var _state$items$id;
47
51
  var id = _ref4.id;
48
- return state.items[id];
52
+ return (_state$items$id = state.items[id]) === null || _state$items$id === void 0 ? void 0 : _state$items$id.data;
49
53
  }
50
54
  });
51
55
  export var useDatasourceActions = createActionsHook(Store);
@@ -0,0 +1,87 @@
1
+ import { type Action } from 'react-sweet-state';
2
+ import { useDatasourceClientExtension } from '@atlaskit/link-client-extension';
3
+ import type { ActionsDiscoveryRequest, AtomicActionInterface } from '@atlaskit/linking-types';
4
+ type IntegrationKey = string;
5
+ type FieldKey = string;
6
+ /**
7
+ * Atomic actions available for an integration (by field)
8
+ * @example
9
+ * ```ts
10
+ * {
11
+ * jira: {
12
+ * summary: {
13
+ * actionKey: 'atlassian:issue:update:summary',
14
+ * type: 'string',
15
+ * description: 'Update issue summary',
16
+ * }
17
+ * }
18
+ * }
19
+ * ```
20
+ */
21
+ type IntegrationActions = Record<IntegrationKey, Record<FieldKey, Pick<AtomicActionInterface, 'actionKey' | 'type' | 'description'>>>;
22
+ /**
23
+ * Permissions available for a target
24
+ */
25
+ type TargetPermissions = Record<FieldKey, {
26
+ isEditable: boolean;
27
+ }>;
28
+ /**
29
+ * User permissions for actions on target (ARI) properties
30
+ * @example
31
+ * ```ts
32
+ * {
33
+ * 'ari:cloud:jira:63cecfe3-16fa-4ee1-8e8d-047cc4b18980:issue/1': {
34
+ * summary: {
35
+ * isEditable: true
36
+ * }
37
+ * }
38
+ * }
39
+ * ```
40
+ */
41
+ type ARI = string;
42
+ type Permissions = Record<ARI, TargetPermissions>;
43
+ export interface ActionsStoreState {
44
+ actionsByIntegration: IntegrationActions;
45
+ permissions: Permissions;
46
+ }
47
+ interface Client {
48
+ getDatasourceActionsAndPermissions: ReturnType<typeof useDatasourceClientExtension>['getDatasourceActionsAndPermissions'];
49
+ }
50
+ export declare const actions: {
51
+ discoverActions: (api: Client, request: ActionsDiscoveryRequest) => Action<ActionsStoreState>;
52
+ };
53
+ export declare const ActionsStore: import("react-sweet-state").Store<ActionsStoreState, {
54
+ discoverActions: (api: Client, request: ActionsDiscoveryRequest) => Action<ActionsStoreState>;
55
+ }>;
56
+ export declare const useDiscoverActions: () => {
57
+ discoverActions: (request: ActionsDiscoveryRequest) => void | Promise<void>;
58
+ };
59
+ /**
60
+ * Retrieves the action schema for a given ARI + fieldKey + integrationKey
61
+ */
62
+ export declare const useAtomicUpdateActionSchema: import("react-sweet-state").HookFunction<Pick<AtomicActionInterface, "actionKey" | "type" | "description"> | undefined, import("react-sweet-state").BoundActions<ActionsStoreState, {
63
+ discoverActions: (api: Client, request: ActionsDiscoveryRequest) => Action<ActionsStoreState>;
64
+ }>, {
65
+ ari: string;
66
+ fieldKey: string;
67
+ integrationKey: string;
68
+ }>;
69
+ /**
70
+ * Given an ARI + fieldKey + integrationKey
71
+ * Returns an executable action that updates a field on the entity if the user has permissions to do so
72
+ *
73
+ * @example
74
+ * ```tsx
75
+ * const { execute } = useExecuteAtomicAction({ ari, fieldKey: 'summary', integrationKey: 'jira' });
76
+ *
77
+ * return <button onClick={() => execute('New summary')}>Update summary</button>;
78
+ * ```
79
+ */
80
+ export declare const useExecuteAtomicAction: ({ ari, fieldKey, integrationKey, }: {
81
+ ari: string;
82
+ fieldKey: string;
83
+ integrationKey: string;
84
+ }) => {
85
+ execute: (value: AtomicActionInterface['type']) => void;
86
+ } | null;
87
+ export {};
@@ -1,21 +1,26 @@
1
1
  /// <reference types="react" />
2
2
  import { type Action } from 'react-sweet-state';
3
- import { type DatasourceDataResponseItem } from '@atlaskit/linking-types';
3
+ import type { DatasourceDataResponseItem } from '@atlaskit/linking-types';
4
+ type UniqueIdentifier = string;
4
5
  export interface State {
5
- items: Record<string, DatasourceDataResponseItem>;
6
+ items: Record<UniqueIdentifier, {
7
+ integrationKey: string | undefined;
8
+ data: DatasourceDataResponseItem;
9
+ }>;
6
10
  }
7
11
  export declare const actions: {
8
- onAddItems: (items: DatasourceDataResponseItem[]) => Action<State, void, string[]>;
12
+ onAddItems: (items: DatasourceDataResponseItem[], integrationKey: string | undefined) => Action<State, void, string[]>;
9
13
  };
10
14
  export declare const Store: import("react-sweet-state").Store<State, {
11
- onAddItems: (items: DatasourceDataResponseItem[]) => Action<State, void, string[]>;
15
+ onAddItems: (items: DatasourceDataResponseItem[], integrationKey: string | undefined) => Action<State, void, string[]>;
12
16
  }>;
13
17
  export declare const useDatasourceItem: import("react-sweet-state").HookStateFunction<DatasourceDataResponseItem | undefined, {
14
18
  id: string;
15
19
  }>;
16
20
  export declare const useDatasourceActions: import("react-sweet-state").HookActionsFunction<import("react-sweet-state").BoundActions<State, {
17
- onAddItems: (items: DatasourceDataResponseItem[]) => Action<State, void, string[]>;
21
+ onAddItems: (items: DatasourceDataResponseItem[], integrationKey: string | undefined) => Action<State, void, string[]>;
18
22
  }>>;
19
23
  export declare const StoreContainer: ({ children }: {
20
24
  children: JSX.Element;
21
25
  }) => JSX.Element;
26
+ export {};
@@ -0,0 +1,87 @@
1
+ import { type Action } from 'react-sweet-state';
2
+ import { useDatasourceClientExtension } from '@atlaskit/link-client-extension';
3
+ import type { ActionsDiscoveryRequest, AtomicActionInterface } from '@atlaskit/linking-types';
4
+ type IntegrationKey = string;
5
+ type FieldKey = string;
6
+ /**
7
+ * Atomic actions available for an integration (by field)
8
+ * @example
9
+ * ```ts
10
+ * {
11
+ * jira: {
12
+ * summary: {
13
+ * actionKey: 'atlassian:issue:update:summary',
14
+ * type: 'string',
15
+ * description: 'Update issue summary',
16
+ * }
17
+ * }
18
+ * }
19
+ * ```
20
+ */
21
+ type IntegrationActions = Record<IntegrationKey, Record<FieldKey, Pick<AtomicActionInterface, 'actionKey' | 'type' | 'description'>>>;
22
+ /**
23
+ * Permissions available for a target
24
+ */
25
+ type TargetPermissions = Record<FieldKey, {
26
+ isEditable: boolean;
27
+ }>;
28
+ /**
29
+ * User permissions for actions on target (ARI) properties
30
+ * @example
31
+ * ```ts
32
+ * {
33
+ * 'ari:cloud:jira:63cecfe3-16fa-4ee1-8e8d-047cc4b18980:issue/1': {
34
+ * summary: {
35
+ * isEditable: true
36
+ * }
37
+ * }
38
+ * }
39
+ * ```
40
+ */
41
+ type ARI = string;
42
+ type Permissions = Record<ARI, TargetPermissions>;
43
+ export interface ActionsStoreState {
44
+ actionsByIntegration: IntegrationActions;
45
+ permissions: Permissions;
46
+ }
47
+ interface Client {
48
+ getDatasourceActionsAndPermissions: ReturnType<typeof useDatasourceClientExtension>['getDatasourceActionsAndPermissions'];
49
+ }
50
+ export declare const actions: {
51
+ discoverActions: (api: Client, request: ActionsDiscoveryRequest) => Action<ActionsStoreState>;
52
+ };
53
+ export declare const ActionsStore: import("react-sweet-state").Store<ActionsStoreState, {
54
+ discoverActions: (api: Client, request: ActionsDiscoveryRequest) => Action<ActionsStoreState>;
55
+ }>;
56
+ export declare const useDiscoverActions: () => {
57
+ discoverActions: (request: ActionsDiscoveryRequest) => void | Promise<void>;
58
+ };
59
+ /**
60
+ * Retrieves the action schema for a given ARI + fieldKey + integrationKey
61
+ */
62
+ export declare const useAtomicUpdateActionSchema: import("react-sweet-state").HookFunction<Pick<AtomicActionInterface, "actionKey" | "type" | "description"> | undefined, import("react-sweet-state").BoundActions<ActionsStoreState, {
63
+ discoverActions: (api: Client, request: ActionsDiscoveryRequest) => Action<ActionsStoreState>;
64
+ }>, {
65
+ ari: string;
66
+ fieldKey: string;
67
+ integrationKey: string;
68
+ }>;
69
+ /**
70
+ * Given an ARI + fieldKey + integrationKey
71
+ * Returns an executable action that updates a field on the entity if the user has permissions to do so
72
+ *
73
+ * @example
74
+ * ```tsx
75
+ * const { execute } = useExecuteAtomicAction({ ari, fieldKey: 'summary', integrationKey: 'jira' });
76
+ *
77
+ * return <button onClick={() => execute('New summary')}>Update summary</button>;
78
+ * ```
79
+ */
80
+ export declare const useExecuteAtomicAction: ({ ari, fieldKey, integrationKey, }: {
81
+ ari: string;
82
+ fieldKey: string;
83
+ integrationKey: string;
84
+ }) => {
85
+ execute: (value: AtomicActionInterface['type']) => void;
86
+ } | null;
87
+ export {};
@@ -1,21 +1,26 @@
1
1
  /// <reference types="react" />
2
2
  import { type Action } from 'react-sweet-state';
3
- import { type DatasourceDataResponseItem } from '@atlaskit/linking-types';
3
+ import type { DatasourceDataResponseItem } from '@atlaskit/linking-types';
4
+ type UniqueIdentifier = string;
4
5
  export interface State {
5
- items: Record<string, DatasourceDataResponseItem>;
6
+ items: Record<UniqueIdentifier, {
7
+ integrationKey: string | undefined;
8
+ data: DatasourceDataResponseItem;
9
+ }>;
6
10
  }
7
11
  export declare const actions: {
8
- onAddItems: (items: DatasourceDataResponseItem[]) => Action<State, void, string[]>;
12
+ onAddItems: (items: DatasourceDataResponseItem[], integrationKey: string | undefined) => Action<State, void, string[]>;
9
13
  };
10
14
  export declare const Store: import("react-sweet-state").Store<State, {
11
- onAddItems: (items: DatasourceDataResponseItem[]) => Action<State, void, string[]>;
15
+ onAddItems: (items: DatasourceDataResponseItem[], integrationKey: string | undefined) => Action<State, void, string[]>;
12
16
  }>;
13
17
  export declare const useDatasourceItem: import("react-sweet-state").HookStateFunction<DatasourceDataResponseItem | undefined, {
14
18
  id: string;
15
19
  }>;
16
20
  export declare const useDatasourceActions: import("react-sweet-state").HookActionsFunction<import("react-sweet-state").BoundActions<State, {
17
- onAddItems: (items: DatasourceDataResponseItem[]) => Action<State, void, string[]>;
21
+ onAddItems: (items: DatasourceDataResponseItem[], integrationKey: string | undefined) => Action<State, void, string[]>;
18
22
  }>>;
19
23
  export declare const StoreContainer: ({ children }: {
20
24
  children: JSX.Element;
21
25
  }) => JSX.Element;
26
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/link-datasource",
3
- "version": "2.9.4",
3
+ "version": "2.9.5",
4
4
  "description": "UI Components to support linking platform dataset feature",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -41,7 +41,7 @@
41
41
  "@atlaskit/avatar-group": "^9.9.0",
42
42
  "@atlaskit/badge": "^16.3.0",
43
43
  "@atlaskit/button": "^19.1.0",
44
- "@atlaskit/datetime-picker": "^13.7.0",
44
+ "@atlaskit/datetime-picker": "^13.8.0",
45
45
  "@atlaskit/dropdown-menu": "^12.15.0",
46
46
  "@atlaskit/editor-prosemirror": "5.0.1",
47
47
  "@atlaskit/empty-state": "^7.9.0",
@@ -54,7 +54,7 @@
54
54
  "@atlaskit/jql-ast": "^3.3.0",
55
55
  "@atlaskit/jql-editor": "^4.4.0",
56
56
  "@atlaskit/jql-editor-autocomplete-rest": "^2.0.0",
57
- "@atlaskit/link-client-extension": "^1.11.0",
57
+ "@atlaskit/link-client-extension": "^1.12.0",
58
58
  "@atlaskit/linking-common": "^5.8.0",
59
59
  "@atlaskit/linking-types": "^8.12.0",
60
60
  "@atlaskit/logo": "^14.1.0",
@@ -98,7 +98,7 @@
98
98
  "@af/integration-testing": "*",
99
99
  "@af/visual-regression": "*",
100
100
  "@atlaskit/link-provider": "^1.14.0",
101
- "@atlaskit/link-test-helpers": "^7.0.0",
101
+ "@atlaskit/link-test-helpers": "^7.1.0",
102
102
  "@atlaskit/ssr": "*",
103
103
  "@atlaskit/visual-regression": "*",
104
104
  "@atlassian/feature-flags-test-utils": "^0.2.0",
@@ -155,6 +155,9 @@
155
155
  "enable_datasource_react_sweet_state": {
156
156
  "type": "boolean"
157
157
  },
158
+ "platform-datasources-enable-two-way-sync": {
159
+ "type": "boolean"
160
+ },
158
161
  "platform.linking-platform.datasource.add-timezone-header": {
159
162
  "type": "boolean",
160
163
  "referenceOnly": "true"
@@ -182,9 +185,6 @@
182
185
  },
183
186
  "platform-datasources-use-refactored-config-modal": {
184
187
  "type": "boolean"
185
- },
186
- "platform-datasources-enable-two-way-sync": {
187
- "type": "boolean"
188
188
  }
189
189
  }
190
190
  }