@atlaskit/link-client-extension 7.3.1 → 8.0.1

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 (41) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/compass.yml +7 -4
  3. package/dist/cjs/services/use-integration-actions-client/build-request-headers.js +40 -0
  4. package/dist/cjs/services/use-integration-actions-client/constants.js +18 -0
  5. package/dist/cjs/services/use-integration-actions-client/get-agg-url.js +34 -0
  6. package/dist/cjs/services/use-integration-actions-client/index.js +143 -0
  7. package/dist/cjs/services/use-integration-actions-client/integration-action-error.js +42 -0
  8. package/dist/cjs/services/use-integration-actions-client/mocks.js +91 -0
  9. package/dist/cjs/services/use-integration-actions-client/parse-execute-integration-action-response.js +168 -0
  10. package/dist/cjs/services/use-integration-actions-client/types.js +1 -0
  11. package/dist/es2019/services/use-integration-actions-client/build-request-headers.js +36 -0
  12. package/dist/es2019/services/use-integration-actions-client/constants.js +29 -0
  13. package/dist/es2019/services/use-integration-actions-client/get-agg-url.js +28 -0
  14. package/dist/es2019/services/use-integration-actions-client/index.js +84 -0
  15. package/dist/es2019/services/use-integration-actions-client/integration-action-error.js +21 -0
  16. package/dist/es2019/services/use-integration-actions-client/mocks.js +85 -0
  17. package/dist/es2019/services/use-integration-actions-client/parse-execute-integration-action-response.js +148 -0
  18. package/dist/es2019/services/use-integration-actions-client/types.js +0 -0
  19. package/dist/esm/services/use-integration-actions-client/build-request-headers.js +35 -0
  20. package/dist/esm/services/use-integration-actions-client/constants.js +12 -0
  21. package/dist/esm/services/use-integration-actions-client/get-agg-url.js +28 -0
  22. package/dist/esm/services/use-integration-actions-client/index.js +136 -0
  23. package/dist/esm/services/use-integration-actions-client/integration-action-error.js +35 -0
  24. package/dist/esm/services/use-integration-actions-client/mocks.js +85 -0
  25. package/dist/esm/services/use-integration-actions-client/parse-execute-integration-action-response.js +161 -0
  26. package/dist/esm/services/use-integration-actions-client/types.js +0 -0
  27. package/dist/types/services/use-data-source-client-extension/index.d.ts +1 -1
  28. package/dist/types/services/use-integration-actions-client/build-request-headers.d.ts +15 -0
  29. package/dist/types/services/use-integration-actions-client/constants.d.ts +9 -0
  30. package/dist/types/services/use-integration-actions-client/get-agg-url.d.ts +13 -0
  31. package/dist/types/services/use-integration-actions-client/index.d.ts +32 -0
  32. package/dist/types/services/use-integration-actions-client/integration-action-error.d.ts +26 -0
  33. package/dist/types/services/use-integration-actions-client/mocks.d.ts +11 -0
  34. package/dist/types/services/use-integration-actions-client/parse-execute-integration-action-response.d.ts +12 -0
  35. package/dist/types/services/use-integration-actions-client/types.d.ts +112 -0
  36. package/package.json +11 -8
  37. package/dist/cjs/entry-points/data-source-client-extension-mocks.js +0 -53
  38. package/dist/es2019/entry-points/data-source-client-extension-mocks.js +0 -25
  39. package/dist/esm/entry-points/data-source-client-extension-mocks.js +0 -25
  40. package/dist/types/entry-points/data-source-client-extension-mocks.d.ts +0 -24
  41. package/use-data-source-client-extension/mocks/package.json +0 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlaskit/link-client-extension
2
2
 
3
+ ## 8.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`95e7e755dad59`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/95e7e755dad59) -
8
+ Add internal Integrations Service actions client (AGG actions.execute) for upcoming 3P Smart Link
9
+ actions. Requests send the AGG `atl-attribution` header. Not exported yet.
10
+
11
+ ## 8.0.0
12
+
13
+ ### Major Changes
14
+
15
+ - [`0c7c7be927bde`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0c7c7be927bde) -
16
+ Remove deprecated Volt re-export shims from public package entry points. Consumers should import
17
+ from the replacement subpaths or implementation entry points instead.
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+
3
23
  ## 7.3.1
4
24
 
5
25
  ### Patch Changes
package/compass.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  configVersion: 1
2
2
  id: ari:cloud:compass:a436116f-02ce-4520-8fbb-7301462a1674:component/c5751cc6-3513-4070-9deb-af31e86aed34/d73d78d0-7881-40ba-a725-b85820bc5e52
3
3
  name: '@atlaskit/link-client-extension'
4
- ownerId: ari:cloud:identity::team/0e6dedf8-18d1-4531-b4b9-07648a425d47 # Eng - Navigation Experiences (Darko Zoroja)
4
+ ownerId: ari:cloud:identity::team/62c5672c-3754-4ebe-8a0f-4d1c87ded5b5 # Rovo Growth - Smartlinks (Rahul Lal)
5
5
  labels:
6
6
  - platform-code
7
7
  - platform-afm
@@ -16,17 +16,17 @@ links:
16
16
  url: https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master
17
17
  - name: Slack Channel
18
18
  type: CHAT_CHANNEL
19
- url: https://atlassian.enterprise.slack.com/archives/CFKGAQZRV # #help-fe-linking-platform
19
+ url: https://atlassian.enterprise.slack.com/archives/C0CBM48P0DS # #help-rovo-growth
20
20
  - name: Link Client Extension
21
21
  type: REPOSITORY
22
22
  url: https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/linking-platform/link-client-extension
23
23
  customFields:
24
24
  - name: Department
25
25
  type: text
26
- value: Eng - Navigation Experiences
26
+ value: Eng - Rovo Search Growth
27
27
  - name: Technical Owner
28
28
  type: user
29
- value: ari:cloud:identity::user/712020:3eb290b3-ff87-40f2-a7ef-4ee9d0ae10bd # Darko Zoroja
29
+ value: ari:cloud:identity::user/712020:297a7015-e760-4d56-aca3-30a62302ef0e # Rahul Lal
30
30
  - name: Required Reviewers Opt In
31
31
  type: boolean
32
32
  value: true
@@ -36,3 +36,6 @@ customFields:
36
36
  - name: Required Reviewer Approvals
37
37
  type: number
38
38
  value: 1
39
+ - name: Trusted Reviewer Teams
40
+ type: text
41
+ value: ari:cloud:identity::team/72f106a3-12dc-4883-ae1d-85623ffee537 # Rovo Growth - Smartlinks - Trusted Reviewers
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildRequestHeaders = void 0;
7
+ var _constants = require("./constants");
8
+ /**
9
+ * Smart Links product → GTM product name, from the AGG Metrics Attribution list.
10
+ * Products without a clear match are left out, so `product` is omitted from the header.
11
+ */
12
+ var GTM_PRODUCT = {
13
+ ATLAS: 'Atlas',
14
+ BITBUCKET: 'Bitbucket Cloud',
15
+ CONFLUENCE: 'Confluence Cloud',
16
+ CSM: 'Customer Service Management',
17
+ JPD: 'Jira Product Discovery',
18
+ JSM: 'Jira Service Management',
19
+ JSW: 'Jira',
20
+ JWM: 'Jira',
21
+ TRELLO: 'Trello'
22
+ };
23
+ /**
24
+ * The `atl-attribution` header AGG asks frontend clients to send for metrics attribution:
25
+ * JSON with `tenantId`, `product` (when known) and `service`.
26
+ *
27
+ * See https://hello.atlassian.net/wiki/spaces/aggp/pages/5954141702
28
+ */
29
+ var buildRequestHeaders = exports.buildRequestHeaders = function buildRequestHeaders(_ref) {
30
+ var cloudId = _ref.cloudId,
31
+ product = _ref.product;
32
+ var attribution = {
33
+ tenantId: cloudId || null,
34
+ product: product ? GTM_PRODUCT[product] : undefined,
35
+ service: _constants.ATTRIBUTION_SERVICE
36
+ };
37
+ return {
38
+ 'atl-attribution': JSON.stringify(attribution)
39
+ };
40
+ };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.USER_CONSENT_REQUIRED = exports.GENERIC_OPERATION_ERROR_MESSAGE = exports.GENERIC_GRAPHQL_ERROR_MESSAGE = exports.EXECUTE_INTEGRATION_ACTION_OPERATION_NAME = exports.EXECUTE_INTEGRATION_ACTION_MUTATION = exports.ATTRIBUTION_SERVICE = void 0;
7
+ var EXECUTE_INTEGRATION_ACTION_OPERATION_NAME = exports.EXECUTE_INTEGRATION_ACTION_OPERATION_NAME = 'LinkClientExtension_ExecuteIntegrationAction';
8
+ var EXECUTE_INTEGRATION_ACTION_MUTATION = exports.EXECUTE_INTEGRATION_ACTION_MUTATION = "mutation ".concat(EXECUTE_INTEGRATION_ACTION_OPERATION_NAME, "(\n\t$actionTypeKey: String\n\t$filter: ActionsExecuteActionFilter!\n\t$actionInput: ActionsExecuteActionInput!\n\t$workspace: String\n) {\n\tactions {\n\t\texecute(\n\t\t\tactionTypeKey: $actionTypeKey\n\t\t\tfilter: $filter\n\t\t\tactionInput: $actionInput\n\t\t\tworkspace: $workspace\n\t\t) {\n\t\t\tstatus\n\t\t\toutputs\n\t\t}\n\t}\n}");
9
+ var USER_CONSENT_REQUIRED = exports.USER_CONSENT_REQUIRED = 'USER_CONSENT_REQUIRED';
10
+
11
+ /** `service` value for the AGG `atl-attribution` header. */
12
+ var ATTRIBUTION_SERVICE = exports.ATTRIBUTION_SERVICE = 'link-client-extension';
13
+
14
+ /** Fallback message when IS reports an error without one. */
15
+ var GENERIC_OPERATION_ERROR_MESSAGE = exports.GENERIC_OPERATION_ERROR_MESSAGE = 'Action failed';
16
+
17
+ /** Fallback message when AGG returns a GraphQL error without one. */
18
+ var GENERIC_GRAPHQL_ERROR_MESSAGE = exports.GENERIC_GRAPHQL_ERROR_MESSAGE = 'GraphQL error';
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getAggUrl = void 0;
7
+ var _getBaseUrl = require("@atlaskit/linking-common/get-base-url");
8
+ var _constants = require("./constants");
9
+ var EDGE_PROXY_AGG_PATH = '/gateway/api/graphql';
10
+
11
+ /**
12
+ * AGG endpoint for the CardClient's environment.
13
+ * - No envKey (running inside a product): same-origin `/gateway/api/graphql`. On the server there
14
+ * is no `window`, so this is relative and requests fail with an `http` error instead of crashing.
15
+ * - `custom`: `baseUrlOverride` is a full object resolver URL (e.g. a direct service URL for SSR),
16
+ * so we can't build an AGG URL from it. Use the relative edge proxy path instead, the same
17
+ * fallback `getResolverUrl` uses.
18
+ * - Other envKeys (e.g. `stg` in examples): the Stargate base URL for that environment + `/graphql`,
19
+ * e.g. `https://pug.jira-dev.com/gateway/api/graphql`.
20
+ * The operation name is added as a query param so requests are easy to identify.
21
+ */
22
+ var getAggUrl = exports.getAggUrl = function getAggUrl(envKey) {
23
+ var graphqlUrl;
24
+ if (envKey === 'custom') {
25
+ graphqlUrl = EDGE_PROXY_AGG_PATH;
26
+ } else if (envKey) {
27
+ var stargateBaseUrl = (0, _getBaseUrl.getBaseUrl)(envKey);
28
+ graphqlUrl = "".concat(stargateBaseUrl.replace(/\/$/, ''), "/graphql");
29
+ } else {
30
+ var origin = (0, _getBaseUrl.getBaseUrl)();
31
+ graphqlUrl = "".concat(origin).concat(EDGE_PROXY_AGG_PATH);
32
+ }
33
+ return "".concat(graphqlUrl, "?operationName=").concat(_constants.EXECUTE_INTEGRATION_ACTION_OPERATION_NAME);
34
+ };
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.useIntegrationActionsClient = void 0;
8
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
11
+ var _react = require("react");
12
+ var _api = require("@atlaskit/linking-common/api");
13
+ var _buildRequestHeaders = require("./build-request-headers");
14
+ var _constants = require("./constants");
15
+ var _getAggUrl = require("./get-agg-url");
16
+ var _integrationActionError = require("./integration-action-error");
17
+ var _parseExecuteIntegrationActionResponse = require("./parse-execute-integration-action-response");
18
+ // Check the shape rather than `instanceof Response`; `instanceof` checks on errors have caused HOTs before.
19
+ var isResponseLike = function isResponseLike(value) {
20
+ return (0, _typeof2.default)(value) === 'object' && value !== null && typeof value.json === 'function';
21
+ };
22
+ var readErrorBody = /*#__PURE__*/function () {
23
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(response) {
24
+ var _t;
25
+ return _regenerator.default.wrap(function (_context) {
26
+ while (1) switch (_context.prev = _context.next) {
27
+ case 0:
28
+ _context.prev = 0;
29
+ _context.next = 1;
30
+ return response.json();
31
+ case 1:
32
+ return _context.abrupt("return", _context.sent);
33
+ case 2:
34
+ _context.prev = 2;
35
+ _t = _context["catch"](0);
36
+ return _context.abrupt("return", undefined);
37
+ case 3:
38
+ case "end":
39
+ return _context.stop();
40
+ }
41
+ }, _callee, null, [[0, 2]]);
42
+ }));
43
+ return function readErrorBody(_x) {
44
+ return _ref.apply(this, arguments);
45
+ };
46
+ }();
47
+ var toHttpError = function toHttpError(error, statusCode) {
48
+ return new _integrationActionError.IntegrationActionError('http', error instanceof Error && error.message ? error.message : "Request failed".concat(statusCode ? " with status ".concat(statusCode) : ''), {
49
+ statusCode: statusCode,
50
+ originalError: error
51
+ });
52
+ };
53
+ /**
54
+ * useIntegrationActionsClient
55
+ *
56
+ * Runs Integrations Service (IS) actions on 3P resources (e.g. listing or adding Google Docs
57
+ * comments) through the AGG `actions { execute }` mutation. Uses a plain fetch rather than Relay,
58
+ * so it works in any host that provides a CardClient.
59
+ *
60
+ * `executeAction` resolves with the action's `outputs`. It throws an `IntegrationActionError` when:
61
+ * - the user needs to connect their 3P account (`kind: 'consent-required'`, with `authUrl`)
62
+ * - IS reports the action failed (`kind: 'operation'`)
63
+ * - AGG returns GraphQL errors (`kind: 'graphql'`)
64
+ * - the request fails (`kind: 'http'`)
65
+ *
66
+ * Each request sends the AGG metrics attribution header (`atl-attribution`), using
67
+ * `actionInput.cloudId` as the tenant.
68
+ *
69
+ * Call `executeAction` from effects or event handlers, not during render (it doesn't run in SSR).
70
+ *
71
+ * @param cardClient used to pick the AGG endpoint for the environment
72
+ * @param options.product product hosting the Smart Link, for metrics attribution
73
+ */
74
+ var useIntegrationActionsClient = exports.useIntegrationActionsClient = function useIntegrationActionsClient(cardClient) {
75
+ var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
76
+ product = _ref2.product;
77
+ var aggUrl = (0, _react.useMemo)(function () {
78
+ return (0, _getAggUrl.getAggUrl)(cardClient === null || cardClient === void 0 ? void 0 : cardClient.envKey);
79
+ }, [cardClient === null || cardClient === void 0 ? void 0 : cardClient.envKey]);
80
+ var executeAction = (0, _react.useCallback)( /*#__PURE__*/function () {
81
+ var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(_ref3) {
82
+ var actionTypeKey, filter, actionInput, workspace, body, _errorBody$errors, _errorBody$data, errorBody, _t2;
83
+ return _regenerator.default.wrap(function (_context2) {
84
+ while (1) switch (_context2.prev = _context2.next) {
85
+ case 0:
86
+ actionTypeKey = _ref3.actionTypeKey, filter = _ref3.filter, actionInput = _ref3.actionInput, workspace = _ref3.workspace;
87
+ _context2.prev = 1;
88
+ _context2.next = 2;
89
+ return (0, _api.request)('post', aggUrl, {
90
+ operationName: _constants.EXECUTE_INTEGRATION_ACTION_OPERATION_NAME,
91
+ query: _constants.EXECUTE_INTEGRATION_ACTION_MUTATION,
92
+ variables: {
93
+ actionTypeKey: actionTypeKey,
94
+ filter: filter,
95
+ actionInput: actionInput,
96
+ workspace: workspace
97
+ }
98
+ }, (0, _buildRequestHeaders.buildRequestHeaders)({
99
+ cloudId: actionInput === null || actionInput === void 0 ? void 0 : actionInput.cloudId,
100
+ product: product
101
+ }), [200]);
102
+ case 2:
103
+ body = _context2.sent;
104
+ _context2.next = 7;
105
+ break;
106
+ case 3:
107
+ _context2.prev = 3;
108
+ _t2 = _context2["catch"](1);
109
+ if (!isResponseLike(_t2)) {
110
+ _context2.next = 6;
111
+ break;
112
+ }
113
+ _context2.next = 4;
114
+ return readErrorBody(_t2);
115
+ case 4:
116
+ errorBody = _context2.sent;
117
+ if (!(errorBody !== null && errorBody !== void 0 && (_errorBody$errors = errorBody.errors) !== null && _errorBody$errors !== void 0 && _errorBody$errors.length || errorBody !== null && errorBody !== void 0 && (_errorBody$data = errorBody.data) !== null && _errorBody$data !== void 0 && (_errorBody$data = _errorBody$data.actions) !== null && _errorBody$data !== void 0 && _errorBody$data.execute)) {
118
+ _context2.next = 5;
119
+ break;
120
+ }
121
+ return _context2.abrupt("return", (0, _parseExecuteIntegrationActionResponse.parseExecuteIntegrationActionResponse)(errorBody, _t2.status));
122
+ case 5:
123
+ throw toHttpError(undefined, _t2.status);
124
+ case 6:
125
+ throw toHttpError(_t2);
126
+ case 7:
127
+ return _context2.abrupt("return", (0, _parseExecuteIntegrationActionResponse.parseExecuteIntegrationActionResponse)(body, 200));
128
+ case 8:
129
+ case "end":
130
+ return _context2.stop();
131
+ }
132
+ }, _callee2, null, [[1, 3]]);
133
+ }));
134
+ return function (_x2) {
135
+ return _ref4.apply(this, arguments);
136
+ };
137
+ }(), [aggUrl, product]);
138
+ return (0, _react.useMemo)(function () {
139
+ return {
140
+ executeAction: executeAction
141
+ };
142
+ }, [executeAction]);
143
+ };
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.IntegrationActionError = void 0;
8
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
9
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
+ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
11
+ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
12
+ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
13
+ var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
14
+ function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
15
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
16
+ /** Thrown by `executeAction` when an action can't be run or fails. */
17
+ var IntegrationActionError = exports.IntegrationActionError = /*#__PURE__*/function (_Error) {
18
+ /** Set when `kind` is `consent-required`: where the user can connect their 3P account. */
19
+
20
+ /** HTTP status, or the status IS reported for the action. */
21
+
22
+ /** The underlying error, when the request itself failed. */
23
+
24
+ function IntegrationActionError(kind, message) {
25
+ var _this;
26
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
27
+ (0, _classCallCheck2.default)(this, IntegrationActionError);
28
+ _this = _callSuper(this, IntegrationActionError, [message]);
29
+ _this.name = 'IntegrationActionError';
30
+ _this.kind = kind;
31
+ _this.authUrl = options.authUrl;
32
+ _this.errorType = options.errorType;
33
+ _this.retryable = options.retryable;
34
+ _this.statusCode = options.statusCode;
35
+ _this.graphQLErrors = options.graphQLErrors;
36
+ _this.operationErrors = options.operationErrors;
37
+ _this.originalError = options.originalError;
38
+ return _this;
39
+ }
40
+ (0, _inherits2.default)(IntegrationActionError, _Error);
41
+ return (0, _createClass2.default)(IntegrationActionError);
42
+ }( /*#__PURE__*/(0, _wrapNativeSuper2.default)(Error));
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.mockExecuteIntegrationActionSuccessResponse = exports.mockExecuteIntegrationActionOperationFailureResponse = exports.mockExecuteIntegrationActionGraphQLErrorResponse = exports.mockExecuteIntegrationActionConsentRequiredResponse = exports.mockAuthUrl = void 0;
7
+ /**
8
+ * Mock AGG `actions { execute }` responses, based on what the ASIMO-5527 spike saw
9
+ * on staging.
10
+ */
11
+
12
+ var mockExecuteIntegrationActionSuccessResponse = exports.mockExecuteIntegrationActionSuccessResponse = {
13
+ data: {
14
+ actions: {
15
+ execute: {
16
+ status: 200,
17
+ outputs: {
18
+ entities: {
19
+ comments: [{
20
+ id: 'comment-1',
21
+ content: 'Looks good to me',
22
+ author: {
23
+ displayName: 'Jane Smith'
24
+ },
25
+ createdTime: '2026-10-27T01:00:00.000Z'
26
+ }]
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ };
33
+ var mockAuthUrl = exports.mockAuthUrl = 'https://id.atlassian.com/outboundAuth/start?key=google-drive';
34
+
35
+ /** IS returns consent errors in `outputs.errors` with HTTP 200 and no GraphQL `errors`. */
36
+ var mockExecuteIntegrationActionConsentRequiredResponse = exports.mockExecuteIntegrationActionConsentRequiredResponse = {
37
+ data: {
38
+ actions: {
39
+ execute: {
40
+ status: 401,
41
+ outputs: {
42
+ errors: [{
43
+ message: 'User consent is required to access this integration',
44
+ statusCode: 401,
45
+ extensions: {
46
+ errorType: 'USER_CONSENT_REQUIRED',
47
+ errorCategory: 'AUTH',
48
+ retryable: false,
49
+ authUrl: mockAuthUrl
50
+ }
51
+ }]
52
+ }
53
+ }
54
+ }
55
+ }
56
+ };
57
+ var mockExecuteIntegrationActionOperationFailureResponse = exports.mockExecuteIntegrationActionOperationFailureResponse = {
58
+ data: {
59
+ actions: {
60
+ execute: {
61
+ status: 404,
62
+ outputs: {
63
+ errors: [{
64
+ message: 'File not found',
65
+ statusCode: 404,
66
+ extensions: {
67
+ errorType: 'NOT_FOUND',
68
+ errorCategory: 'CLIENT',
69
+ retryable: false
70
+ }
71
+ }]
72
+ }
73
+ }
74
+ }
75
+ }
76
+ };
77
+ var mockExecuteIntegrationActionGraphQLErrorResponse = exports.mockExecuteIntegrationActionGraphQLErrorResponse = {
78
+ data: {
79
+ actions: {
80
+ execute: null
81
+ }
82
+ },
83
+ errors: [{
84
+ message: 'Action not found for filter',
85
+ path: ['actions', 'execute'],
86
+ extensions: {
87
+ classification: 'DataFetchingException',
88
+ statusCode: 400
89
+ }
90
+ }]
91
+ };
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.parseExecuteIntegrationActionResponse = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
10
+ var _constants = require("./constants");
11
+ var _integrationActionError = require("./integration-action-error");
12
+ 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; }
13
+ 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; }
14
+ var isRecord = function isRecord(value) {
15
+ return (0, _typeof2.default)(value) === 'object' && value !== null && !Array.isArray(value);
16
+ };
17
+ var isSuccessStatus = function isSuccessStatus(status) {
18
+ return status >= 200 && status < 300;
19
+ };
20
+ var getStringExtension = function getStringExtension(extensions, key) {
21
+ var value = extensions === null || extensions === void 0 ? void 0 : extensions[key];
22
+ return typeof value === 'string' && value !== '' ? value : undefined;
23
+ };
24
+
25
+ /**
26
+ * Returns `authUrl` from the extensions only if it's an absolute `https:` URL, so a malformed or
27
+ * unsafe value (e.g. `javascript:`) is never handed to the UI to open.
28
+ */
29
+ var getAuthUrl = function getAuthUrl(extensions) {
30
+ var value = getStringExtension(extensions, 'authUrl');
31
+ if (!value) {
32
+ return undefined;
33
+ }
34
+ try {
35
+ return new URL(value).protocol === 'https:' ? value : undefined;
36
+ } catch (_unused) {
37
+ return undefined;
38
+ }
39
+ };
40
+
41
+ /** Normalises an entry of `outputs.errors`, which IS doesn't guarantee is well-formed. */
42
+ var toOperationErrorEntry = function toOperationErrorEntry(error) {
43
+ if (typeof error === 'string') {
44
+ return {
45
+ message: error || _constants.GENERIC_OPERATION_ERROR_MESSAGE
46
+ };
47
+ }
48
+ if (!isRecord(error)) {
49
+ return {
50
+ message: _constants.GENERIC_OPERATION_ERROR_MESSAGE
51
+ };
52
+ }
53
+ return _objectSpread(_objectSpread({
54
+ message: typeof error.message === 'string' && error.message !== '' ? error.message : _constants.GENERIC_OPERATION_ERROR_MESSAGE
55
+ }, isRecord(error.extensions) ? {
56
+ extensions: error.extensions
57
+ } : {}), typeof error.statusCode === 'number' ? {
58
+ statusCode: error.statusCode
59
+ } : {});
60
+ };
61
+
62
+ /** Normalises an entry of the GraphQL `errors` array, which may be malformed. */
63
+ var toGraphQLErrorEntry = function toGraphQLErrorEntry(error) {
64
+ if (!isRecord(error)) {
65
+ return {
66
+ message: typeof error === 'string' && error ? error : _constants.GENERIC_GRAPHQL_ERROR_MESSAGE
67
+ };
68
+ }
69
+ return _objectSpread(_objectSpread({
70
+ message: typeof error.message === 'string' && error.message !== '' ? error.message : _constants.GENERIC_GRAPHQL_ERROR_MESSAGE
71
+ }, isRecord(error.extensions) ? {
72
+ extensions: error.extensions
73
+ } : {}), Array.isArray(error.path) ? {
74
+ path: error.path
75
+ } : {});
76
+ };
77
+
78
+ /** A non-empty `outputs.errors` always means the action failed, even if the entries are malformed. */
79
+ var getOperationErrors = function getOperationErrors(outputs) {
80
+ var errors = isRecord(outputs) ? outputs.errors : undefined;
81
+ if (!Array.isArray(errors) || errors.length === 0) {
82
+ return undefined;
83
+ }
84
+ return errors.map(toOperationErrorEntry);
85
+ };
86
+ var toOperationError = function toOperationError(operationErrors, status) {
87
+ var _error$extensions, _error$statusCode;
88
+ var consentError = operationErrors.find(function (error) {
89
+ return getStringExtension(error.extensions, 'errorType') === _constants.USER_CONSENT_REQUIRED || !!getAuthUrl(error.extensions);
90
+ });
91
+ var error = consentError !== null && consentError !== void 0 ? consentError : operationErrors[0];
92
+ var authUrl = getAuthUrl(consentError === null || consentError === void 0 ? void 0 : consentError.extensions);
93
+ var retryable = (_error$extensions = error.extensions) === null || _error$extensions === void 0 ? void 0 : _error$extensions.retryable;
94
+ var options = {
95
+ authUrl: authUrl,
96
+ errorType: getStringExtension(error.extensions, 'errorType'),
97
+ retryable: typeof retryable === 'boolean' ? retryable : undefined,
98
+ statusCode: (_error$statusCode = error.statusCode) !== null && _error$statusCode !== void 0 ? _error$statusCode : status,
99
+ operationErrors: operationErrors
100
+ };
101
+ if (consentError && authUrl) {
102
+ return new _integrationActionError.IntegrationActionError('consent-required', error.message, options);
103
+ }
104
+ return new _integrationActionError.IntegrationActionError('operation', error.message, options);
105
+ };
106
+ var toGraphQLError = function toGraphQLError(graphQLErrors, statusCode) {
107
+ var authUrl = graphQLErrors.map(function (error) {
108
+ return getAuthUrl(error.extensions);
109
+ }).find(function (url) {
110
+ return !!url;
111
+ });
112
+ var errorType = graphQLErrors.map(function (error) {
113
+ var _getStringExtension;
114
+ return (_getStringExtension = getStringExtension(error.extensions, 'errorType')) !== null && _getStringExtension !== void 0 ? _getStringExtension : getStringExtension(error.extensions, 'classification');
115
+ }).find(function (type) {
116
+ return !!type;
117
+ });
118
+ var message = graphQLErrors.map(function (error) {
119
+ return error.message;
120
+ }).join('; ');
121
+ var options = {
122
+ authUrl: authUrl,
123
+ errorType: errorType,
124
+ statusCode: statusCode,
125
+ graphQLErrors: graphQLErrors
126
+ };
127
+ if (authUrl) {
128
+ return new _integrationActionError.IntegrationActionError('consent-required', message, options);
129
+ }
130
+ return new _integrationActionError.IntegrationActionError('graphql', message, options);
131
+ };
132
+
133
+ /**
134
+ * Turns an AGG response body into the action response, or throws an `IntegrationActionError`.
135
+ *
136
+ * Errors are checked in this order:
137
+ * 1. IS operation errors in `outputs.errors` (consent required, or the action failed).
138
+ * 2. GraphQL `errors`, even with a partial `execute` result. The mutation only selects
139
+ * `actions.execute`, so any GraphQL error relates to the action.
140
+ * 3. A missing `execute` result.
141
+ * 4. A non-2xx `status` from IS.
142
+ */
143
+ var parseExecuteIntegrationActionResponse = exports.parseExecuteIntegrationActionResponse = function parseExecuteIntegrationActionResponse(body, httpStatus) {
144
+ var _body$data;
145
+ var execute = body === null || body === void 0 || (_body$data = body.data) === null || _body$data === void 0 || (_body$data = _body$data.actions) === null || _body$data === void 0 ? void 0 : _body$data.execute;
146
+ var graphQLErrors = Array.isArray(body === null || body === void 0 ? void 0 : body.errors) && body.errors.length ? body.errors.map(toGraphQLErrorEntry) : undefined;
147
+ var operationErrors = getOperationErrors(execute === null || execute === void 0 ? void 0 : execute.outputs);
148
+ if (operationErrors !== null && operationErrors !== void 0 && operationErrors.length) {
149
+ throw toOperationError(operationErrors, execute === null || execute === void 0 ? void 0 : execute.status);
150
+ }
151
+ if (graphQLErrors) {
152
+ throw toGraphQLError(graphQLErrors, httpStatus);
153
+ }
154
+ if (!execute) {
155
+ throw new _integrationActionError.IntegrationActionError('graphql', 'No action result returned', {
156
+ statusCode: httpStatus
157
+ });
158
+ }
159
+ if (typeof execute.status === 'number' && !isSuccessStatus(execute.status)) {
160
+ throw new _integrationActionError.IntegrationActionError('operation', "Action failed with status ".concat(execute.status), {
161
+ statusCode: execute.status
162
+ });
163
+ }
164
+ return {
165
+ status: execute.status,
166
+ outputs: execute.outputs
167
+ };
168
+ };
@@ -0,0 +1,36 @@
1
+ import { ATTRIBUTION_SERVICE } from './constants';
2
+
3
+ /**
4
+ * Smart Links product → GTM product name, from the AGG Metrics Attribution list.
5
+ * Products without a clear match are left out, so `product` is omitted from the header.
6
+ */
7
+ const GTM_PRODUCT = {
8
+ ATLAS: 'Atlas',
9
+ BITBUCKET: 'Bitbucket Cloud',
10
+ CONFLUENCE: 'Confluence Cloud',
11
+ CSM: 'Customer Service Management',
12
+ JPD: 'Jira Product Discovery',
13
+ JSM: 'Jira Service Management',
14
+ JSW: 'Jira',
15
+ JWM: 'Jira',
16
+ TRELLO: 'Trello'
17
+ };
18
+ /**
19
+ * The `atl-attribution` header AGG asks frontend clients to send for metrics attribution:
20
+ * JSON with `tenantId`, `product` (when known) and `service`.
21
+ *
22
+ * See https://hello.atlassian.net/wiki/spaces/aggp/pages/5954141702
23
+ */
24
+ export const buildRequestHeaders = ({
25
+ cloudId,
26
+ product
27
+ }) => {
28
+ const attribution = {
29
+ tenantId: cloudId || null,
30
+ product: product ? GTM_PRODUCT[product] : undefined,
31
+ service: ATTRIBUTION_SERVICE
32
+ };
33
+ return {
34
+ 'atl-attribution': JSON.stringify(attribution)
35
+ };
36
+ };