From be99c17ab48079f7a808c6337c495e5a4960a55a Mon Sep 17 00:00:00 2001 From: Kayub Maharjan Date: Tue, 5 Dec 2023 16:26:31 +1100 Subject: [PATCH 01/26] - Modal added for backfilling and disconnecting subscription --- .../Connections/GHCloudConnections/index.tsx | 59 ++++++++++++++++--- .../Modals/DisconnectSubscriptionModal.tsx | 44 ++++++++++++++ .../Modals/RestartBackfillModal.tsx | 42 +++++++++++++ spa/src/utils/dynamicTableHelper.tsx | 42 +++++++++++-- src/rest-interfaces/index.ts | 2 + 5 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx create mode 100644 spa/src/pages/Connections/Modals/RestartBackfillModal.tsx diff --git a/spa/src/pages/Connections/GHCloudConnections/index.tsx b/spa/src/pages/Connections/GHCloudConnections/index.tsx index 9492ca30d9..1983b34420 100644 --- a/spa/src/pages/Connections/GHCloudConnections/index.tsx +++ b/spa/src/pages/Connections/GHCloudConnections/index.tsx @@ -1,11 +1,16 @@ /** @jsxImportSource @emotion/react */ +import { useState } from "react"; import { DynamicTableStateless } from "@atlaskit/dynamic-table"; import { head, getGHSubscriptionsRows, } from "../../../utils/dynamicTableHelper"; -import { GhCloudSubscriptions } from "../../../rest-interfaces"; +import { BackfillPageModalTypes, GhCloudSubscriptions } from "../../../rest-interfaces"; import { Box, xcss } from "@atlaskit/primitives"; +import { SuccessfulConnection } from "rest-interfaces"; +import DisconnectSubscriptionModal from "../Modals/DisconnectSubscriptionModal"; +import RestartBackfillModal from "../Modals/RestartBackfillModal"; +import { ModalTransition } from "@atlaskit/modal-dialog"; const containerStyles = xcss({ display: "flex", @@ -15,18 +20,54 @@ const containerStyles = xcss({ type GitHubCloudConnectionsProps = { ghCloudSubscriptions: GhCloudSubscriptions; }; + const GitHubCloudConnections = ({ ghCloudSubscriptions, }: GitHubCloudConnectionsProps) => { + const [isModalOpened, setIsModalOpened] = useState(false); + const [subscriptionForModal, setSubscriptionForModal] = useState(undefined); + const [selectedModal, setSelectedModal] = useState("BACKFILL"); + + const openedModal = () => { + switch (selectedModal) { + case "BACKFILL": + return (); + case "DISCONNECT_SUBSCRIPTION": + return ; + // TODO: Create modals for GHE later + case "DISCONNECT_SERVER": + case "DISCONNECT_SERVER_APP": + default: + return <>; + } + }; + return ( - - - + <> + + + + + + { + isModalOpened && subscriptionForModal && openedModal() + } + + ); }; diff --git a/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx b/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx new file mode 100644 index 0000000000..a5ddf794f8 --- /dev/null +++ b/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx @@ -0,0 +1,44 @@ +import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from "@atlaskit/modal-dialog"; +import Button from "@atlaskit/button"; +import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; + +/** + * NOTE: While testing in dev mode, please disable the React.StrictMode first, + * otherwise this modal won't show up. + */ +const DisconnectSubscriptionModal = ({ subscription, setIsModalOpened }: { + subscription: SuccessfulConnection, + setIsModalOpened: (x: boolean) => void +}) => { + const disconnect = () => { + // TODO: API call to disconnect this subscription + console.log("Disconnect", subscription.account.login); + setIsModalOpened(false); + }; + + return ( + <> + setIsModalOpened(false)}> + + + <>Disconnect {subscription.account.login}? + + + +

+ Are you sure you want to disconnect your organization {subscription.account.login}? + This means that you will have to redo the backfill of historical data if you ever want to reconnect +

+
+ + + + +
+ + ); +}; + +export default DisconnectSubscriptionModal; diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx new file mode 100644 index 0000000000..302b05ed45 --- /dev/null +++ b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx @@ -0,0 +1,42 @@ +import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from "@atlaskit/modal-dialog"; +import Button from "@atlaskit/button"; +import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; + +/** + * NOTE: While testing in dev mode, please disable the React.StrictMode first, + * otherwise this modal won't show up. + */ +const RestartBackfillModal = ({ subscription, setIsModalOpened }: { + subscription: SuccessfulConnection, + setIsModalOpened: (x: boolean) => void +}) => { + const backfill = () => { + // TODO: API call to disconnect this subscription + console.log("Backfill for", subscription.account.login); + setIsModalOpened(false); + }; + + return ( + <> + setIsModalOpened(false)}> + + Backfill your data + + +

+ Backfilling data can take a long time, so we’ll only backfill your data from the last 6 months. + If you want to backfill more data, choose a date below. Branches will be backfilled regardless of their age. +

+
+ + + + +
+ + ); +}; + +export default RestartBackfillModal; diff --git a/spa/src/utils/dynamicTableHelper.tsx b/spa/src/utils/dynamicTableHelper.tsx index 1a62a23094..96dac29f03 100644 --- a/spa/src/utils/dynamicTableHelper.tsx +++ b/spa/src/utils/dynamicTableHelper.tsx @@ -3,7 +3,7 @@ import Avatar from "@atlaskit/avatar"; import Badge from "@atlaskit/badge"; import { token } from "@atlaskit/tokens"; import Lozenge from "@atlaskit/lozenge"; -import { SuccessfulConnection } from "../rest-interfaces"; +import { BackfillPageModalTypes, SuccessfulConnection } from "../rest-interfaces"; import { ThemeAppearance } from "@atlaskit/lozenge/dist/types/Lozenge"; import { css } from "@emotion/react"; @@ -13,6 +13,12 @@ type Row = { cells: { key: string | number; content: React.JSX.Element | string | number }[]; }; +type ConnectionsActionsCallback = { + setIsModalOpened: (x: boolean) => void; + setSubscriptionForModal: (sub: SuccessfulConnection) => void; + setSelectedModal: (x: BackfillPageModalTypes) => void; +}; + const rowWrapperStyle = css` display: flex; align-items: center; @@ -57,14 +63,19 @@ const createHead = (withWidth: boolean) => { width: withWidth ? 30 : undefined, }, { - key: "party", + key: "repos", content: "Repos", width: withWidth ? 30 : undefined, }, { - key: "term", + key: "status", content: "Status", width: withWidth ? 30 : undefined, + }, + { + key: "options", + content: "Settings", + width: withWidth ? 10: undefined, } ], }; @@ -73,7 +84,8 @@ const createHead = (withWidth: boolean) => { export const head = createHead(true); export const getGHSubscriptionsRows = ( - SuccessfulConnections: SuccessfulConnection[] + SuccessfulConnections: SuccessfulConnection[], + callbacks?: ConnectionsActionsCallback ): Row[] => { if (!SuccessfulConnections) { return []; @@ -147,6 +159,28 @@ export const getGHSubscriptionsRows = ( ), + }, + { + key: cloudConnection.id, + content: ( + <> + {/* TODO: Convert this into a dropdown */} +
{ + callbacks?.setIsModalOpened(true); + callbacks?.setSubscriptionForModal(cloudConnection); + callbacks?.setSelectedModal("DISCONNECT_SUBSCRIPTION"); + }}> + Disconnect +
+
{ + callbacks?.setIsModalOpened(true); + callbacks?.setSubscriptionForModal(cloudConnection); + callbacks?.setSelectedModal("BACKFILL"); + }}> + Backfill +
+ + ) } ], }) diff --git a/src/rest-interfaces/index.ts b/src/rest-interfaces/index.ts index 4e6da157d7..2e6881d41f 100644 --- a/src/rest-interfaces/index.ts +++ b/src/rest-interfaces/index.ts @@ -178,3 +178,5 @@ export type GHSubscriptions = { ghCloudSubscriptions: GhCloudSubscriptions; ghEnterpriseServers: GhEnterpriseServer[]; }; + +export type BackfillPageModalTypes = "BACKFILL" | "DISCONNECT_SUBSCRIPTION" | "DISCONNECT_SERVER_APP" | "DISCONNECT_SERVER"; From 150023edab63082a14a4aa6347809410d3232372 Mon Sep 17 00:00:00 2001 From: Kayub Maharjan Date: Tue, 5 Dec 2023 17:24:43 +1100 Subject: [PATCH 02/26] - Fixing eslint in a test case --- spa/src/services/oauth-manager/test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spa/src/services/oauth-manager/test.ts b/spa/src/services/oauth-manager/test.ts index 70c5240210..c7f067ec75 100644 --- a/spa/src/services/oauth-manager/test.ts +++ b/spa/src/services/oauth-manager/test.ts @@ -17,13 +17,13 @@ describe("AuthManager", () => { const mockRedirectUrlOnce = (state: string) => { when(Api.auth.generateOAuthUrl) .calledWith() - .mockResolvedValueOnce({ data: { redirectUrl: REDIRECT_URL, state: state } } as any); - } + .mockResolvedValueOnce({ data: { redirectUrl: REDIRECT_URL, state: state } } as never); + }; const mockExchangeTokenOnce= (code: string, state: string, accessToken: string) => { when(Api.auth.exchangeToken) .calledWith(code, state) - .mockResolvedValueOnce({ data: { accessToken } } as any); + .mockResolvedValueOnce({ data: { accessToken } } as never); }; const onWinCloseAndBlock = { onWinClosed: () => {}, onPopupBlocked: () => {} }; From 4dd1eed2252d20255ba9b53840429b26e278f1bc Mon Sep 17 00:00:00 2001 From: Kayub Maharjan Date: Tue, 5 Dec 2023 17:25:25 +1100 Subject: [PATCH 03/26] - WIP - backfill modal --- spa/package.json | 1 + .../Modals/RestartBackfillModal.tsx | 13 +- spa/yarn.lock | 126 +++++++++++++++++- 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/spa/package.json b/spa/package.json index 46bce986a2..0f16327116 100644 --- a/spa/package.json +++ b/spa/package.json @@ -25,6 +25,7 @@ "@atlaskit/avatar": "^21.3.9", "@atlaskit/badge": "^15.1.14", "@atlaskit/button": "^16.8.0", + "@atlaskit/checkbox": "^13.0.1", "@atlaskit/css-reset": "^6.5.2", "@atlaskit/dynamic-table": "^14.11.5", "@atlaskit/form": "^8.11.8", diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx index 302b05ed45..b35085a8e2 100644 --- a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx +++ b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx @@ -1,6 +1,8 @@ import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from "@atlaskit/modal-dialog"; import Button from "@atlaskit/button"; import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; +import { Checkbox } from "@atlaskit/checkbox"; +import { useState } from "react"; /** * NOTE: While testing in dev mode, please disable the React.StrictMode first, @@ -10,9 +12,11 @@ const RestartBackfillModal = ({ subscription, setIsModalOpened }: { subscription: SuccessfulConnection, setIsModalOpened: (x: boolean) => void }) => { + const [restartFromDateCheck, setRestartFromDateCheck] = useState(false); + const backfill = () => { // TODO: API call to disconnect this subscription - console.log("Backfill for", subscription.account.login); + console.log("Backfill for", subscription.account.login, restartFromDateCheck); setIsModalOpened(false); }; @@ -27,6 +31,13 @@ const RestartBackfillModal = ({ subscription, setIsModalOpened }: { Backfilling data can take a long time, so we’ll only backfill your data from the last 6 months. If you want to backfill more data, choose a date below. Branches will be backfilled regardless of their age.

+

+ setRestartFromDateCheck(!restartFromDateCheck)} + label={`Restart the backfill from today to this date`} + name="restart-from-selected-date" + /> +

diff --git a/spa/yarn.lock b/spa/yarn.lock index 09b7ab384a..530e6f8687 100644 --- a/spa/yarn.lock +++ b/spa/yarn.lock @@ -120,6 +120,20 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" +"@atlaskit/checkbox@^13.0.1": + version "13.0.1" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/checkbox/-/checkbox-13.0.1.tgz#316ed8382a6fcee66f60a676c96f24bbc6af67af" + integrity sha512-/KkaC02GnG4XoQ93Zvy5TbfDGFN2VyD00VuHg8s/y9hPuzEbFxdh6GVB/6IYMXqQ5xRkJb22X7W5cw7miCkTuA== + dependencies: + "@atlaskit/analytics-next" "^9.1.0" + "@atlaskit/ds-lib" "^2.2.0" + "@atlaskit/icon" "^22.0.0" + "@atlaskit/platform-feature-flags" "^0.2.0" + "@atlaskit/theme" "^12.6.0" + "@atlaskit/tokens" "^1.28.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + "@atlaskit/codemod-utils@^4.2.0": version "4.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/codemod-utils/-/codemod-utils-4.2.3.tgz#3f217fb7e6ad9f638ab03bdaa10d2704d2a26952" @@ -224,6 +238,15 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" +"@atlaskit/icon@^22.0.0": + version "22.0.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/icon/-/icon-22.0.0.tgz#f267fe623827eb4ddf3b1632756049d04d19a8b7" + integrity sha512-90sfTQ7O6vehzNY8Qm2SCufwmQQ3A5Pw40yHBEHD5XeiJ0BaGY8fmzxx16JcqLxiUfyDlxml3rsKD++XzksOHg== + dependencies: + "@atlaskit/tokens" "^1.28.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + "@atlaskit/in-product-testing@^0.2.0": version "0.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/in-product-testing/-/in-product-testing-0.2.3.tgz#b56c583951b2c6f5eeb1dced8ff8eefa1cd9cb08" @@ -501,6 +524,18 @@ "@babel/types" "^7.20.0" bind-event-listener "^2.1.1" +"@atlaskit/tokens@^1.28.0": + version "1.29.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/tokens/-/tokens-1.29.0.tgz#812ddab4d1baacec80f57934b92823bb9fcfd6ba" + integrity sha512-VDSl6+HKwInNt9mHXO2M2wKlWDo9rCOFhcAbXD+V3z5AsM7ccjX7Dl9Z2H9lciV6o+pkS0FkdE83DfJVgCBiRg== + dependencies: + "@atlaskit/ds-lib" "^2.2.0" + "@atlaskit/platform-feature-flags" "^0.2.0" + "@babel/runtime" "^7.0.0" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.20.0" + bind-event-listener "^2.1.1" + "@atlaskit/tokens@^1.4.0", "@atlaskit/tokens@^1.8.0": version "1.10.1" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/tokens/-/tokens-1.10.1.tgz#28e32d1205bf7f771e0f523dea17fbecac297bdb" @@ -565,6 +600,14 @@ dependencies: "@babel/highlight" "^7.22.5" +"@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + "@babel/compat-data@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" @@ -646,6 +689,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.23.5": + version "7.23.5" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/generator/-/generator-7.23.5.tgz#17d0a1ea6b62f351d281350a5f80b87a810c4755" + integrity sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA== + dependencies: + "@babel/types" "^7.23.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -717,6 +770,11 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-environment-visitor@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" @@ -730,6 +788,14 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.5" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -839,6 +905,16 @@ resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" @@ -885,11 +961,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.5": + version "7.23.5" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/parser/-/parser-7.23.5.tgz#37dee97c4752af148e1d38c34b856b2507660563" + integrity sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ== + "@babel/parser@^7.22.7": version "7.22.7" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" @@ -1750,6 +1840,15 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -1791,6 +1890,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.23.2": + version "7.23.5" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/traverse/-/traverse-7.23.5.tgz#f546bf9aba9ef2b042c0e00d245990c15508e7ec" + integrity sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.5" + "@babel/types" "^7.23.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" @@ -1800,6 +1915,15 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.23.0", "@babel/types@^7.23.5": + version "7.23.5" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/types/-/types-7.23.5.tgz#48d730a00c95109fa4393352705954d74fb5b602" + integrity sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -4416,7 +4540,7 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://packages.atlassian.com/api/npm/npm-remote/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== -chalk@^2.0.0, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://packages.atlassian.com/api/npm/npm-remote/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== From 7377807860db49c6fe80b60f887c72407f391776 Mon Sep 17 00:00:00 2001 From: Kayub Maharjan Date: Tue, 5 Dec 2023 22:56:50 +1100 Subject: [PATCH 04/26] - WIP - backfill modal --- spa/package.json | 1 + .../Modals/RestartBackfillModal.tsx | 15 +- spa/yarn.lock | 167 +++++++++++++++++- 3 files changed, 180 insertions(+), 3 deletions(-) diff --git a/spa/package.json b/spa/package.json index 0f16327116..45f2ba921a 100644 --- a/spa/package.json +++ b/spa/package.json @@ -27,6 +27,7 @@ "@atlaskit/button": "^16.8.0", "@atlaskit/checkbox": "^13.0.1", "@atlaskit/css-reset": "^6.5.2", + "@atlaskit/datetime-picker": "^13.0.2", "@atlaskit/dynamic-table": "^14.11.5", "@atlaskit/form": "^8.11.8", "@atlaskit/heading": "^1.3.7", diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx index b35085a8e2..3710816636 100644 --- a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx +++ b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx @@ -1,8 +1,10 @@ +import { useState } from "react"; import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from "@atlaskit/modal-dialog"; import Button from "@atlaskit/button"; import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; import { Checkbox } from "@atlaskit/checkbox"; -import { useState } from "react"; +import { Label } from "@atlaskit/form"; +// import { DatePicker } from "@atlaskit/datetime-picker"; /** * NOTE: While testing in dev mode, please disable the React.StrictMode first, @@ -24,13 +26,22 @@ const RestartBackfillModal = ({ subscription, setIsModalOpened }: { <> setIsModalOpened(false)}> - Backfill your data + Backfill your data

Backfilling data can take a long time, so we’ll only backfill your data from the last 6 months. If you want to backfill more data, choose a date below. Branches will be backfilled regardless of their age.

+

+ + {/* TODO: This datepicker is throwing a dependency issue saying module not found, add it later when fixed*/} + {/**/} +

setRestartFromDateCheck(!restartFromDateCheck)} diff --git a/spa/yarn.lock b/spa/yarn.lock index 530e6f8687..46fe9c2888 100644 --- a/spa/yarn.lock +++ b/spa/yarn.lock @@ -51,6 +51,15 @@ prop-types "^15.5.10" use-memo-one "^1.1.1" +"@atlaskit/app-provider@^0.4.0": + version "0.4.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/app-provider/-/app-provider-0.4.0.tgz#33d9706bd81a8799b041ab36ecb2537f1b57b09c" + integrity sha512-ZvDVRSHD19rxKvx+YomWvekvtPzNbZEwYdHGXWG3QjdnP+1oBEeaVgkI9LnpWAoreunoQ8xUgmJ6g2qAYBjnoA== + dependencies: + "@atlaskit/tokens" "^1.28.0" + "@babel/runtime" "^7.0.0" + bind-event-listener "^2.1.1" + "@atlaskit/avatar@^21.3.9": version "21.3.9" resolved "https://registry.yarnpkg.com/@atlaskit/avatar/-/avatar-21.3.9.tgz#01049b9730d0374a4352728bc08aba638e0011e6" @@ -87,6 +96,25 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" +"@atlaskit/button@^16.17.0": + version "16.17.10" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/button/-/button-16.17.10.tgz#8e305dc7ff926f44ee1acd51f29fdd270cb5fd05" + integrity sha512-C7UIVAKKrHOtwHiLT94XfyqQ/EAvWq4C7BxaxY0iTJZCvBTgZcefuHLsol9j612IaT7B8NJ/+vnwkKFpGjvFkQ== + dependencies: + "@atlaskit/analytics-next" "^9.1.0" + "@atlaskit/ds-lib" "^2.2.0" + "@atlaskit/focus-ring" "^1.3.0" + "@atlaskit/icon" "^22.0.0" + "@atlaskit/interaction-context" "^2.1.0" + "@atlaskit/platform-feature-flags" "^0.2.0" + "@atlaskit/primitives" "^1.13.0" + "@atlaskit/spinner" "^16.0.0" + "@atlaskit/theme" "^12.6.0" + "@atlaskit/tokens" "^1.29.0" + "@atlaskit/visually-hidden" "^1.2.4" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + "@atlaskit/button@^16.8.0": version "16.8.0" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/button/-/button-16.8.0.tgz#85afd61b219f4a6b25524e61f9ca3da769a438b7" @@ -120,6 +148,26 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" +"@atlaskit/calendar@^14.0.0": + version "14.0.2" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/calendar/-/calendar-14.0.2.tgz#df392186a2d64cbc6edab77affead0436c2fb4ad" + integrity sha512-v81dRWJ0EVGV2H+VVE3IPLaOehJxVcK/xIk9He1WgjN0fN/1Ejmmt96+9oxo6FbR8prSg4IDGMpUyJrBzgOg0g== + dependencies: + "@atlaskit/analytics-next" "^9.1.0" + "@atlaskit/button" "^16.17.0" + "@atlaskit/ds-explorations" "^3.0.0" + "@atlaskit/ds-lib" "^2.2.0" + "@atlaskit/heading" "^1.4.0" + "@atlaskit/icon" "^22.0.0" + "@atlaskit/locale" "^2.6.0" + "@atlaskit/primitives" "^1.13.0" + "@atlaskit/theme" "^12.6.0" + "@atlaskit/tokens" "^1.29.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + date-fns "^2.17.0" + react-uid "^2.2.0" + "@atlaskit/checkbox@^13.0.1": version "13.0.1" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/checkbox/-/checkbox-13.0.1.tgz#316ed8382a6fcee66f60a676c96f24bbc6af67af" @@ -151,6 +199,28 @@ "@babel/runtime" "^7.0.0" fbjs "^3.0.0" +"@atlaskit/datetime-picker@^13.0.2": + version "13.0.2" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/datetime-picker/-/datetime-picker-13.0.2.tgz#6ce8899117134801dd7ef89730b6c4b77307ec48" + integrity sha512-kz/h+/dTT8QyajUm4nWt7BTfQo1D5efpqennHQNfjck70Y0TApNVZSnesFaVn3hNn1EaLwJ5KsubtQqdSUjiiA== + dependencies: + "@atlaskit/analytics-next" "^9.1.0" + "@atlaskit/calendar" "^14.0.0" + "@atlaskit/ds-lib" "^2.2.0" + "@atlaskit/icon" "^22.0.0" + "@atlaskit/layering" "^0.2.0" + "@atlaskit/locale" "^2.6.0" + "@atlaskit/platform-feature-flags" "^0.2.0" + "@atlaskit/popper" "^5.5.0" + "@atlaskit/select" "^17.0.0" + "@atlaskit/theme" "^12.6.0" + "@atlaskit/tokens" "^1.29.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + date-fns "^2.17.0" + lodash "^4.17.21" + react-scrolllock "^5.0.1" + "@atlaskit/ds-explorations@^2.2.0": version "2.2.12" resolved "https://registry.yarnpkg.com/@atlaskit/ds-explorations/-/ds-explorations-2.2.12.tgz#6f6060b34b4f9843f2bf01d25e99dce12fc4839e" @@ -161,6 +231,16 @@ "@emotion/react" "^11.7.1" tiny-invariant "^1.2.0" +"@atlaskit/ds-explorations@^3.0.0": + version "3.0.6" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/ds-explorations/-/ds-explorations-3.0.6.tgz#ebe7097bc491bdd080133175efc8b99a3d8b70e0" + integrity sha512-JIZtkGvUlfVZENDTqhKuI/SXOntD4Cy+V12jUqYZQPNtyv8WlfOUErj+1y0lxKQKYI4wOhflKwK2rTtWmJOQsQ== + dependencies: + "@atlaskit/tokens" "^1.29.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + tiny-invariant "^1.2.0" + "@atlaskit/ds-lib@^2.2.0": version "2.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/ds-lib/-/ds-lib-2.2.3.tgz#fc65a829b45ee0a26c9c6c97072e2d570214aec7" @@ -218,6 +298,15 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" +"@atlaskit/heading@^1.4.0": + version "1.4.3" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/heading/-/heading-1.4.3.tgz#dccf63117fe0600c1994010eb4a5a97214f3ca41" + integrity sha512-29VbpehvlUmAel4SqS+KFcGj2/y5M7ZMfW2VWHlOfU0JDEL0GCd+RBwqg23iVsSf2k9N77yE6CJ7HSRmWS1aSQ== + dependencies: + "@atlaskit/tokens" "^1.25.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + "@atlaskit/icon@^21.12.0": version "21.12.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/icon/-/icon-21.12.5.tgz#834c967b0e62ceb6bb4951a36a50fda773a5f9ce" @@ -270,6 +359,14 @@ "@babel/runtime" "^7.0.0" bind-event-listener "^2.1.1" +"@atlaskit/locale@^2.6.0": + version "2.6.2" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/locale/-/locale-2.6.2.tgz#14c7ed315306963b5e1e421bbe62f74a30d3f219" + integrity sha512-MFjig0nQMRB/baMF+8LPJOXmWE1DJqYFPO1bfr0t2giQRhDKZxyvyTLKuij+T/PTK70iSyFzn7bLTNwvPXqhMA== + dependencies: + "@atlaskit/select" "^17.0.0" + "@babel/runtime" "^7.0.0" + "@atlaskit/lozenge@^11.4.3": version "11.4.3" resolved "https://registry.yarnpkg.com/@atlaskit/lozenge/-/lozenge-11.4.3.tgz#bf9ea032bbdc94bd9b24167fa88e150b86426f6a" @@ -388,6 +485,19 @@ "@emotion/serialize" "^1.1.0" bind-event-listener "^2.1.1" +"@atlaskit/primitives@^1.13.0": + version "1.13.1" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/primitives/-/primitives-1.13.1.tgz#c6da57952e06b56439ec0a27f55d722314c1c564" + integrity sha512-mjtL6tma8/PdsWG+F6qCgYLSziZcKo4r/VtdMmCNpVgAU+YJda3HczFey+roLNrtzVMZMKg4msm4SO8UqHi05A== + dependencies: + "@atlaskit/app-provider" "^0.4.0" + "@atlaskit/tokens" "^1.29.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + "@emotion/serialize" "^1.1.0" + bind-event-listener "^2.1.1" + tiny-invariant "^1.2.0" + "@atlaskit/primitives@^1.6.0": version "1.6.7" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/primitives/-/primitives-1.6.7.tgz#03217a8497341c6fefc673638a4a58f2a1f1b5c9" @@ -425,6 +535,31 @@ react-uid "^2.2.0" shallow-equal "^1.0.0" +"@atlaskit/select@^17.0.0": + version "17.0.1" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/select/-/select-17.0.1.tgz#04de803672673ada760101f149d622a04275cb91" + integrity sha512-vh4UALGNoislGUSVt4VZXSrgYpGWnQn5jTQwlSkrX3hutkM3z/0P8YvoEdFPIAJHdu2ryg2nq3P1w7mwLwjWTQ== + dependencies: + "@atlaskit/analytics-next" "^9.1.0" + "@atlaskit/icon" "^22.0.0" + "@atlaskit/platform-feature-flags" "^0.2.0" + "@atlaskit/spinner" "^16.0.0" + "@atlaskit/theme" "^12.6.0" + "@atlaskit/tokens" "^1.28.0" + "@atlaskit/visually-hidden" "^1.2.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + "@popperjs/core" "^2.9.1" + bind-event-listener "^2.1.1" + memoize-one "^6.0.0" + react-fast-compare "^3.2.0" + react-focus-lock "^2.9.5" + react-node-resolver "^1.0.1" + react-popper "^2.2.3" + react-select "^5.4.0" + react-uid "^2.2.0" + shallow-equal "^3.1.0" + "@atlaskit/skeleton@^0.2.3": version "0.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/skeleton/-/skeleton-0.2.3.tgz#e56db536f01adaeb0dfc9eb39478337c09315e96" @@ -456,6 +591,17 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" +"@atlaskit/spinner@^16.0.0": + version "16.0.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/spinner/-/spinner-16.0.0.tgz#e59f2bea3aaafaf5b0e23999d96cb1c5fc777a90" + integrity sha512-KZq+YiwO9lb9VFHBZisVq5/ToyhkhxgLSmPIIm8K+mHoTt7CGk9+Iz04Oui5OYg6Sq5XN1SVklWCuDEhiJcmlQ== + dependencies: + "@atlaskit/interaction-context" "^2.1.0" + "@atlaskit/theme" "^12.6.0" + "@atlaskit/tokens" "^1.28.0" + "@babel/runtime" "^7.0.0" + "@emotion/react" "^11.7.1" + "@atlaskit/textarea@^4.7.7": version "4.7.7" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/textarea/-/textarea-4.7.7.tgz#eafa1096f8cdbe65bff26e9b82ac241236bf7d80" @@ -524,7 +670,7 @@ "@babel/types" "^7.20.0" bind-event-listener "^2.1.1" -"@atlaskit/tokens@^1.28.0": +"@atlaskit/tokens@^1.28.0", "@atlaskit/tokens@^1.29.0": version "1.29.0" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/tokens/-/tokens-1.29.0.tgz#812ddab4d1baacec80f57934b92823bb9fcfd6ba" integrity sha512-VDSl6+HKwInNt9mHXO2M2wKlWDo9rCOFhcAbXD+V3z5AsM7ccjX7Dl9Z2H9lciV6o+pkS0FkdE83DfJVgCBiRg== @@ -1840,6 +1986,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.21.0": + version "7.23.5" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/runtime/-/runtime-7.23.5.tgz#11edb98f8aeec529b82b211028177679144242db" + integrity sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15": version "7.22.15" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -5097,6 +5250,13 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" +date-fns@^2.17.0: + version "2.30.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== + dependencies: + "@babel/runtime" "^7.21.0" + debug@2.6.9, debug@^2.6.0: version "2.6.9" resolved "https://packages.atlassian.com/api/npm/npm-remote/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -10398,6 +10558,11 @@ shallow-equal@^1.0.0: resolved "https://packages.atlassian.com/api/npm/npm-remote/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== +shallow-equal@^3.1.0: + version "3.1.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/shallow-equal/-/shallow-equal-3.1.0.tgz#e7a54bac629c7f248eff6c2f5b63122ba4320bec" + integrity sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg== + shebang-command@^2.0.0: version "2.0.0" resolved "https://packages.atlassian.com/api/npm/npm-remote/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" From 54747ffcb0885c680815595d02acbc672a695c2d Mon Sep 17 00:00:00 2001 From: Kayub Maharjan Date: Wed, 6 Dec 2023 10:46:54 +1100 Subject: [PATCH 05/26] - WIP - backfill modal --- spa/src/pages/Connections/Modals/RestartBackfillModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx index 3710816636..15d5eaeb3a 100644 --- a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx +++ b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx @@ -38,7 +38,7 @@ const RestartBackfillModal = ({ subscription, setIsModalOpened }: { {/* TODO: This datepicker is throwing a dependency issue saying module not found, add it later when fixed*/} {/**/}

From 85c0ec911eb4430d46b790a478d0ac44ceaf3a6d Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 12:50:33 +1100 Subject: [PATCH 06/26] chore: add new endpoint for backfill --- spa/src/api/subscriptions/index.ts | 9 ++- src/rest/rest-router.ts | 13 ++++ src/rest/routes/sync/index.ts | 90 ++++++++++++++++++++++++++ src/routes/jira/sync/jira-sync-post.ts | 29 +-------- src/util/github-sync-helper.ts | 26 ++++++++ 5 files changed, 140 insertions(+), 27 deletions(-) create mode 100644 src/rest/routes/sync/index.ts create mode 100644 src/util/github-sync-helper.ts diff --git a/spa/src/api/subscriptions/index.ts b/spa/src/api/subscriptions/index.ts index 0c5c2b8d0c..e0b1dcbe00 100644 --- a/spa/src/api/subscriptions/index.ts +++ b/spa/src/api/subscriptions/index.ts @@ -1,5 +1,12 @@ import { axiosRest } from "../axiosInstance"; export default { - getSubscriptions: () => axiosRest.get("/rest/subscriptions") + getSubscriptions: () => axiosRest.get("/rest/subscriptions"), + syncSubscriptions: (data: { + installationId: number; + commitsFromDate: string; + appId?: number; + source: string; + syncType: string; + }) => axiosRest.post("/rest/sync", data), }; diff --git a/src/rest/rest-router.ts b/src/rest/rest-router.ts index 04b972aa15..3e9920cb7d 100644 --- a/src/rest/rest-router.ts +++ b/src/rest/rest-router.ts @@ -1,5 +1,6 @@ import { Router } from "express"; import { JwtHandler } from "./middleware/jwt/jwt-handler"; +import { body } from "express-validator"; import { OAuthRouter } from "./routes/oauth"; import { OAuthCallbackHandler, OrgsInstalledHandler, OrgsInstallRequestedHandler } from "./routes/github-callback"; import { GitHubOrgsRouter } from "./routes/github-orgs"; @@ -10,6 +11,7 @@ import { RestErrorHandler } from "./middleware/error"; import { JiraAdminEnforceMiddleware } from "./middleware/jira-admin/jira-admin-check"; import { AnalyticsProxyHandler } from "./routes/analytics-proxy"; import { SubscriptionsRouter } from "./routes/subscriptions"; +import { SyncRouter } from "./routes/sync"; import { DeferredRouter } from "./routes/deferred"; export const RestRouter = Router({ mergeParams: true }); @@ -21,6 +23,17 @@ const subRouter = Router({ mergeParams: true }); */ RestRouter.use("/subscriptions", JwtHandler, JiraAdminEnforceMiddleware, SubscriptionsRouter); +/** + * Separate route for SPA to start backfill of both cloud and server subscriptions + */ +RestRouter.use( + "/sync", + body("commitsFromDate").optional().isISO8601(), + JwtHandler, + JiraAdminEnforceMiddleware, + SyncRouter +); + /** * For cloud flow, the path will be `/rest/app/cloud/XXX`, * For enterprise flow, the path will be `/rest/app/SERVER-UUID/XXX` diff --git a/src/rest/routes/sync/index.ts b/src/rest/routes/sync/index.ts new file mode 100644 index 0000000000..16f88dfe28 --- /dev/null +++ b/src/rest/routes/sync/index.ts @@ -0,0 +1,90 @@ +import { Router, Request, Response, NextFunction } from "express"; +import { ParamsDictionary } from "express-serve-static-core"; +import { errorWrapper } from "../../helper"; +import { Subscription } from "models/subscription"; +import { booleanFlag, BooleanFlags } from "config/feature-flags"; +import { findOrStartSync } from "~/src/sync/sync-utils"; +import { determineSyncTypeAndTargetTasks } from "~/src/util/github-sync-helper"; +import { BaseLocals } from ".."; +import { InvalidArgumentError } from "~/src/config/errors"; + +export const SyncRouter = Router(); + +interface RequestBody { + installationId: number; + appId: number; + syncType: string; + source: string; + commitsFromDate: string; +} + +const restSyncPost = async ( + req: Request, + res: Response, + next: NextFunction +) => { + const { + installationId: gitHubInstallationId, + appId: gitHubAppId, + syncType: syncTypeFromReq, + source + } = req.body; + // A date to start fetching commit history(main and branch) from. + const commitsFromDate = req.body.commitsFromDate + ? new Date(req.body.commitsFromDate) + : undefined; + + const logAdditionalData = await booleanFlag( + BooleanFlags.VERBOSE_LOGGING, + res.locals.installation.jiraHost + ); + + logAdditionalData + ? req.log.info( + { gitHubInstallationId }, + "verbose logging - Received sync request on rest route") + : req.log.info("Received sync request on rest route"); + + try { + const subscription = await Subscription.getSingleInstallation( + res.locals.installation.jiraHost, + gitHubInstallationId, + gitHubAppId + ); + if (!subscription) { + req.log.info( + { + jiraHost: res.locals.installation.jiraHost, + installationId: gitHubInstallationId + }, + "Subscription not found when retrying sync." + ); + throw new InvalidArgumentError("Subscription not found, cannot resync."); + } + + if (commitsFromDate && commitsFromDate.valueOf() > Date.now()) { + throw new InvalidArgumentError( + "Invalid date value, cannot select a future date!" + ); + } + + const { syncType, targetTasks } = determineSyncTypeAndTargetTasks( + syncTypeFromReq, + subscription + ); + await findOrStartSync( + subscription, + req.log, + syncType, + commitsFromDate || subscription.backfillSince, + targetTasks, + { source } + ); + + res.sendStatus(202); + } catch (error: unknown) { + next(new Error("Unauthorized")); + } +}; + +SyncRouter.post("/", errorWrapper("SubscriptionsGet", restSyncPost)); diff --git a/src/routes/jira/sync/jira-sync-post.ts b/src/routes/jira/sync/jira-sync-post.ts index 4dbee9efa7..0df0412efe 100644 --- a/src/routes/jira/sync/jira-sync-post.ts +++ b/src/routes/jira/sync/jira-sync-post.ts @@ -1,11 +1,11 @@ -import { Subscription, SyncStatus } from "models/subscription"; +import { Subscription } from "models/subscription"; import * as Sentry from "@sentry/node"; import { NextFunction, Request, Response } from "express"; import { findOrStartSync } from "~/src/sync/sync-utils"; import { sendAnalytics } from "utils/analytics-client"; import { AnalyticsEventTypes, AnalyticsTrackEventsEnum, AnalyticsTrackSource } from "interfaces/common"; -import { TaskType, SyncType } from "~/src/sync/sync.types"; import { booleanFlag, BooleanFlags } from "config/feature-flags"; +import { determineSyncTypeAndTargetTasks, getStartTimeInDaysAgo } from "../../../util/github-sync-helper"; export const JiraSyncPost = async (req: Request, res: Response, next: NextFunction): Promise => { const { installationId: gitHubInstallationId, appId: gitHubAppId, syncType: syncTypeFromReq, source } = req.body; @@ -35,7 +35,7 @@ export const JiraSyncPost = async (req: Request, res: Response, next: NextFuncti return; } - const { syncType, targetTasks } = await determineSyncTypeAndTargetTasks(syncTypeFromReq, subscription); + const { syncType, targetTasks } = determineSyncTypeAndTargetTasks(syncTypeFromReq, subscription); await findOrStartSync(subscription, req.log, syncType, commitsFromDate || subscription.backfillSince, targetTasks, { source }); await sendAnalytics(res.locals.jiraHost, AnalyticsEventTypes.TrackEvent, { @@ -64,26 +64,3 @@ export const JiraSyncPost = async (req: Request, res: Response, next: NextFuncti next(new Error("Unauthorized")); } }; - -const MILLISECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000; -const getStartTimeInDaysAgo = (commitsFromDate: Date | undefined) => { - if (commitsFromDate === undefined) return undefined; - return Math.floor((Date.now() - commitsFromDate?.getTime()) / MILLISECONDS_IN_ONE_DAY); -}; - -type SyncTypeAndTargetTasks = { - syncType: SyncType, - targetTasks: TaskType[] | undefined, -}; - -const determineSyncTypeAndTargetTasks = async (syncTypeFromReq: string, subscription: Subscription): Promise => { - if (syncTypeFromReq === "full") { - return { syncType: "full", targetTasks: undefined }; - } - - if (subscription.syncStatus === SyncStatus.FAILED) { - return { syncType: "full", targetTasks: undefined }; - } - - return { syncType: "partial", targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"] }; -}; diff --git a/src/util/github-sync-helper.ts b/src/util/github-sync-helper.ts new file mode 100644 index 0000000000..6c89d10d95 --- /dev/null +++ b/src/util/github-sync-helper.ts @@ -0,0 +1,26 @@ +import { Subscription, SyncStatus } from "models/subscription"; +import { TaskType, SyncType } from "~/src/sync/sync.types"; + + +const MILLISECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000; +export const getStartTimeInDaysAgo = (commitsFromDate: Date | undefined) => { + if (commitsFromDate === undefined) return undefined; + return Math.floor((Date.now() - commitsFromDate?.getTime()) / MILLISECONDS_IN_ONE_DAY); +}; + +type SyncTypeAndTargetTasks = { + syncType: SyncType, + targetTasks: TaskType[] | undefined, +}; + +export const determineSyncTypeAndTargetTasks = (syncTypeFromReq: string, subscription: Subscription): SyncTypeAndTargetTasks => { + if (syncTypeFromReq === "full") { + return { syncType: "full", targetTasks: undefined }; + } + + if (subscription.syncStatus === SyncStatus.FAILED) { + return { syncType: "full", targetTasks: undefined }; + } + + return { syncType: "partial", targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"] }; +}; \ No newline at end of file From b8a040269ef33be7cceeebc1b2df15d55725adb0 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 12:55:24 +1100 Subject: [PATCH 07/26] chore: add new endpoint for backfill --- test/snapshots/app.test.ts.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/snapshots/app.test.ts.snap b/test/snapshots/app.test.ts.snap index 359304d482..71a5bcd6da 100644 --- a/test/snapshots/app.test.ts.snap +++ b/test/snapshots/app.test.ts.snap @@ -15,6 +15,8 @@ exports[`app getFrontendApp please review routes and update snapshot when adding query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,serveStatic :GET ^/?(?=/|$)^/rest/?(?=/|$)^/subscriptions/?(?=/|$)^/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionsGet +:POST ^/?(?=/|$)^/rest/?(?=/|$)^/sync/?(?=/|$)^/?$ + query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,middleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionsGet :GET ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/github-callback/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,OAuthCallbackHandler :GET ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/github-installed/?$ From d2b2ce1961ff9caa4a8a6a91214d36bfa55c2b43 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 13:26:25 +1100 Subject: [PATCH 08/26] chore: add new endpoint for backfill --- spa/src/api/subscriptions/index.ts | 3 +-- src/rest/rest-router.ts | 17 +++++++---------- src/rest/routes/sync/index.ts | 10 ++++++---- src/util/github-sync-helper.ts | 2 +- test/snapshots/app.test.ts.snap | 4 ++-- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/spa/src/api/subscriptions/index.ts b/spa/src/api/subscriptions/index.ts index e0b1dcbe00..45ae098554 100644 --- a/spa/src/api/subscriptions/index.ts +++ b/spa/src/api/subscriptions/index.ts @@ -5,8 +5,7 @@ export default { syncSubscriptions: (data: { installationId: number; commitsFromDate: string; - appId?: number; source: string; syncType: string; - }) => axiosRest.post("/rest/sync", data), + }) => axiosRest.post(`/rest/app/cloud/sync`, data), }; diff --git a/src/rest/rest-router.ts b/src/rest/rest-router.ts index 3e9920cb7d..4e44e4b449 100644 --- a/src/rest/rest-router.ts +++ b/src/rest/rest-router.ts @@ -11,7 +11,7 @@ import { RestErrorHandler } from "./middleware/error"; import { JiraAdminEnforceMiddleware } from "./middleware/jira-admin/jira-admin-check"; import { AnalyticsProxyHandler } from "./routes/analytics-proxy"; import { SubscriptionsRouter } from "./routes/subscriptions"; -import { SyncRouter } from "./routes/sync"; +import { SyncRouterHandler } from "./routes/sync"; import { DeferredRouter } from "./routes/deferred"; export const RestRouter = Router({ mergeParams: true }); @@ -24,22 +24,19 @@ const subRouter = Router({ mergeParams: true }); RestRouter.use("/subscriptions", JwtHandler, JiraAdminEnforceMiddleware, SubscriptionsRouter); /** - * Separate route for SPA to start backfill of both cloud and server subscriptions + * For cloud flow, the path will be `/rest/app/cloud/XXX`, + * For enterprise flow, the path will be `/rest/app/SERVER-UUID/XXX` */ -RestRouter.use( +RestRouter.use("/app/:cloudOrUUID", subRouter); + +subRouter.post( "/sync", body("commitsFromDate").optional().isISO8601(), JwtHandler, JiraAdminEnforceMiddleware, - SyncRouter + SyncRouterHandler ); -/** - * For cloud flow, the path will be `/rest/app/cloud/XXX`, - * For enterprise flow, the path will be `/rest/app/SERVER-UUID/XXX` - */ -RestRouter.use("/app/:cloudOrUUID", subRouter); - subRouter.get("/github-callback", OAuthCallbackHandler); subRouter.get("/github-installed", OrgsInstalledHandler); subRouter.get("/github-requested", OrgsInstallRequestedHandler); diff --git a/src/rest/routes/sync/index.ts b/src/rest/routes/sync/index.ts index 16f88dfe28..5c138849d6 100644 --- a/src/rest/routes/sync/index.ts +++ b/src/rest/routes/sync/index.ts @@ -1,4 +1,4 @@ -import { Router, Request, Response, NextFunction } from "express"; +import { Request, Response, NextFunction } from "express"; import { ParamsDictionary } from "express-serve-static-core"; import { errorWrapper } from "../../helper"; import { Subscription } from "models/subscription"; @@ -8,8 +8,6 @@ import { determineSyncTypeAndTargetTasks } from "~/src/util/github-sync-helper"; import { BaseLocals } from ".."; import { InvalidArgumentError } from "~/src/config/errors"; -export const SyncRouter = Router(); - interface RequestBody { installationId: number; appId: number; @@ -23,6 +21,10 @@ const restSyncPost = async ( res: Response, next: NextFunction ) => { + //TODO: We are yet to handle enterprise backfill + // const cloudOrUUID = req.params.cloudOrUUID; + // const gheUUID = cloudOrUUID === "cloud" ? undefined : "some-ghe-uuid"; + const { installationId: gitHubInstallationId, appId: gitHubAppId, @@ -87,4 +89,4 @@ const restSyncPost = async ( } }; -SyncRouter.post("/", errorWrapper("SubscriptionsGet", restSyncPost)); +export const SyncRouterHandler = errorWrapper("AnalyticsProxyHandler",restSyncPost); diff --git a/src/util/github-sync-helper.ts b/src/util/github-sync-helper.ts index 6c89d10d95..6ba4115d68 100644 --- a/src/util/github-sync-helper.ts +++ b/src/util/github-sync-helper.ts @@ -5,7 +5,7 @@ import { TaskType, SyncType } from "~/src/sync/sync.types"; const MILLISECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000; export const getStartTimeInDaysAgo = (commitsFromDate: Date | undefined) => { if (commitsFromDate === undefined) return undefined; - return Math.floor((Date.now() - commitsFromDate?.getTime()) / MILLISECONDS_IN_ONE_DAY); + return Math.floor((Date.now() - commitsFromDate.getTime()) / MILLISECONDS_IN_ONE_DAY); }; type SyncTypeAndTargetTasks = { diff --git a/test/snapshots/app.test.ts.snap b/test/snapshots/app.test.ts.snap index 71a5bcd6da..cc98cdc0b4 100644 --- a/test/snapshots/app.test.ts.snap +++ b/test/snapshots/app.test.ts.snap @@ -15,8 +15,8 @@ exports[`app getFrontendApp please review routes and update snapshot when adding query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,serveStatic :GET ^/?(?=/|$)^/rest/?(?=/|$)^/subscriptions/?(?=/|$)^/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionsGet -:POST ^/?(?=/|$)^/rest/?(?=/|$)^/sync/?(?=/|$)^/?$ - query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,middleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionsGet +:POST ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/sync/?$ + query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,middleware,JwtHandler,jiraAdminEnforceMiddleware,AnalyticsProxyHandler :GET ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/github-callback/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,OAuthCallbackHandler :GET ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/github-installed/?$ From 2a2fcac0c1568d9d2c07d1cb16cdecbc9aa46cdc Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 14:32:32 +1100 Subject: [PATCH 09/26] chore: add test cases --- src/rest/routes/sync/sync.test.ts | 162 ++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/rest/routes/sync/sync.test.ts diff --git a/src/rest/routes/sync/sync.test.ts b/src/rest/routes/sync/sync.test.ts new file mode 100644 index 0000000000..8e693ae804 --- /dev/null +++ b/src/rest/routes/sync/sync.test.ts @@ -0,0 +1,162 @@ +import { getFrontendApp } from "~/src/app"; +import { Installation } from "models/installation"; +import { Subscription } from "models/subscription"; +import express, { Express } from "express"; +import { RootRouter } from "routes/router"; +import supertest from "supertest"; +import { encodeSymmetric } from "atlassian-jwt"; +import { GitHubServerApp } from "models/github-server-app"; +import { v4 as newUUID } from "uuid"; +import { sqsQueues } from "~/src/sqs/queues"; + +jest.mock("~/src/sqs/queues"); +jest.mock("config/feature-flags"); + +describe("Checking the deferred request parsing route", () => { + let app: Express; + let installation: Installation; + const installationIdForCloud = 1; + const installationIdForServer = 2; + let gitHubServerApp: GitHubServerApp; + // let jwt: string; + const testSharedSecret = "test-secret"; + const clientKey = "jira-client-key"; + const getToken = ({ + secret = testSharedSecret, + iss = clientKey, + exp = Date.now() / 1000 + 10000, + qsh = "context-qsh", + sub = "myAccount" } = {}): string => { + return encodeSymmetric({ + qsh, + iss, + exp, + sub + }, secret); + }; + beforeEach(async () => { + app = getFrontendApp(); + installation = await Installation.install({ + host: jiraHost, + sharedSecret: testSharedSecret, + clientKey: clientKey + }); + await Subscription.install({ + installationId: installationIdForCloud, + host: jiraHost, + hashedClientKey: installation.clientKey, + gitHubAppId: undefined + }); + gitHubServerApp = await GitHubServerApp.install({ + uuid: newUUID(), + appId: 123, + gitHubAppName: "My GitHub Server App", + gitHubBaseUrl: gheUrl, + gitHubClientId: "lvl.1234", + gitHubClientSecret: "myghsecret", + webhookSecret: "mywebhooksecret", + privateKey: "myprivatekey", + installationId: installation.id + }, jiraHost); + await Subscription.install({ + installationId: installationIdForServer, + host: jiraHost, + hashedClientKey: installation.clientKey, + gitHubAppId: gitHubServerApp.id + }); + app = express(); + app.use(RootRouter); + + // jwt = encodeSymmetric({ + // qsh: "context-qsh", + // iss: "jira-client-key" + // }, await installation.decrypt("encryptedSharedSecret", getLogger("test"))); + }); + + describe("cloud", () => { + it("should throw 401 error when no github token is passed", async () => { + const resp = await supertest(app) + .get(`/rest/app/cloud/sync`); + + expect(resp.status).toEqual(401); + }); + + it("should return 200 on correct post for /rest/app/cloud/sync one for Cloud app", async () => { + return supertest(app) + .post("/rest/app/cloud/sync") + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForCloud, + jiraHost + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + installationId: installationIdForCloud, + jiraHost, + startTime: expect.anything(), + gitHubAppConfig: expect.objectContaining({ gitHubAppId: undefined, uuid: undefined }) + }), expect.anything(), expect.anything()); + }); + }); + + it("should run incremental sync", async() => { + const commitsFromDate = new Date(new Date().getTime() - 2000); + const backfillSince = new Date(new Date().getTime() - 1000); + const subscription = await Subscription.getSingleInstallation( + jiraHost, + installationIdForServer, + gitHubServerApp.id + ); + await subscription?.update({ + syncStatus: "COMPLETE", + backfillSince + }); + return supertest(app) + .post("/rest/app/cloud/sync") + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForServer, + jiraHost, + appId: gitHubServerApp.id, + commitsFromDate + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + syncType: "partial", + installationId: installationIdForServer, + jiraHost, + commitsFromDate: commitsFromDate.toISOString(), + targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"], + gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) + }), expect.anything(), expect.anything()); + }); + }); + + it("should run full sync if explicitly selected by user", async () => { + const commitsFromDate = new Date(new Date().getTime() - 2000); + return supertest(app) + .post("/rest/app/cloud/sync") + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForServer, + jiraHost, + appId: gitHubServerApp.id, + commitsFromDate, + syncType: "full" + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + syncType: "full", + installationId: installationIdForServer, + jiraHost, + commitsFromDate: commitsFromDate.toISOString(), + targetTasks: undefined, + gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) + }), expect.anything(), expect.anything()); + }); + }); + }); +}); From 8675da76bc0e2bdfda51b58a99223806c75085f2 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 14:48:04 +1100 Subject: [PATCH 10/26] chore: add test cases --- src/rest/routes/sync/{sync.test.ts => index.test.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/rest/routes/sync/{sync.test.ts => index.test.ts} (99%) diff --git a/src/rest/routes/sync/sync.test.ts b/src/rest/routes/sync/index.test.ts similarity index 99% rename from src/rest/routes/sync/sync.test.ts rename to src/rest/routes/sync/index.test.ts index 8e693ae804..6515d297e6 100644 --- a/src/rest/routes/sync/sync.test.ts +++ b/src/rest/routes/sync/index.test.ts @@ -76,7 +76,7 @@ describe("Checking the deferred request parsing route", () => { describe("cloud", () => { it("should throw 401 error when no github token is passed", async () => { const resp = await supertest(app) - .get(`/rest/app/cloud/sync`); + .get("/rest/app/cloud/sync"); expect(resp.status).toEqual(401); }); From 1ed4f9bf241c722c36474d232511661ed863dea6 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 15:17:46 +1100 Subject: [PATCH 11/26] chore: PR comments --- spa/src/api/subscriptions/index.ts | 8 ++---- src/rest-interfaces/index.ts | 6 +++++ src/rest/routes/sync/index.ts | 42 +++++++++--------------------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/spa/src/api/subscriptions/index.ts b/spa/src/api/subscriptions/index.ts index 45ae098554..50e96bc4e0 100644 --- a/spa/src/api/subscriptions/index.ts +++ b/spa/src/api/subscriptions/index.ts @@ -1,11 +1,7 @@ +import { RestSyncReqBody } from "~/src/rest-interfaces"; import { axiosRest } from "../axiosInstance"; export default { getSubscriptions: () => axiosRest.get("/rest/subscriptions"), - syncSubscriptions: (data: { - installationId: number; - commitsFromDate: string; - source: string; - syncType: string; - }) => axiosRest.post(`/rest/app/cloud/sync`, data), + syncSubscriptions: (data: RestSyncReqBody) => axiosRest.post(`/rest/app/cloud/sync`, data), }; diff --git a/src/rest-interfaces/index.ts b/src/rest-interfaces/index.ts index 4e6da157d7..ec248527c0 100644 --- a/src/rest-interfaces/index.ts +++ b/src/rest-interfaces/index.ts @@ -1,3 +1,9 @@ +export type RestSyncReqBody = { + installationId: number; + syncType: string; + source: string; + commitsFromDate: string; +} export type GetRedirectUrlResponse = { redirectUrl: string; diff --git a/src/rest/routes/sync/index.ts b/src/rest/routes/sync/index.ts index 5c138849d6..1aa79c0295 100644 --- a/src/rest/routes/sync/index.ts +++ b/src/rest/routes/sync/index.ts @@ -1,33 +1,28 @@ -import { Request, Response, NextFunction } from "express"; +import { Request, Response } from "express"; import { ParamsDictionary } from "express-serve-static-core"; import { errorWrapper } from "../../helper"; import { Subscription } from "models/subscription"; -import { booleanFlag, BooleanFlags } from "config/feature-flags"; import { findOrStartSync } from "~/src/sync/sync-utils"; import { determineSyncTypeAndTargetTasks } from "~/src/util/github-sync-helper"; import { BaseLocals } from ".."; -import { InvalidArgumentError } from "~/src/config/errors"; +import { InvalidArgumentError, RestApiError } from "~/src/config/errors"; +import { RestSyncReqBody } from "~/src/rest-interfaces"; -interface RequestBody { - installationId: number; - appId: number; - syncType: string; - source: string; - commitsFromDate: string; -} const restSyncPost = async ( - req: Request, - res: Response, - next: NextFunction + req: Request, + res: Response ) => { //TODO: We are yet to handle enterprise backfill - // const cloudOrUUID = req.params.cloudOrUUID; - // const gheUUID = cloudOrUUID === "cloud" ? undefined : "some-ghe-uuid"; + const cloudOrUUID = req.params.cloudOrUUID; + const gheUUID = cloudOrUUID === "cloud" ? undefined : req.params.cloudOrUUID; + let gitHubAppId : number | undefined = undefined; + if (gheUUID) { + gitHubAppId = parseFloat(gheUUID); + } const { installationId: gitHubInstallationId, - appId: gitHubAppId, syncType: syncTypeFromReq, source } = req.body; @@ -36,17 +31,6 @@ const restSyncPost = async ( ? new Date(req.body.commitsFromDate) : undefined; - const logAdditionalData = await booleanFlag( - BooleanFlags.VERBOSE_LOGGING, - res.locals.installation.jiraHost - ); - - logAdditionalData - ? req.log.info( - { gitHubInstallationId }, - "verbose logging - Received sync request on rest route") - : req.log.info("Received sync request on rest route"); - try { const subscription = await Subscription.getSingleInstallation( res.locals.installation.jiraHost, @@ -61,7 +45,7 @@ const restSyncPost = async ( }, "Subscription not found when retrying sync." ); - throw new InvalidArgumentError("Subscription not found, cannot resync."); + throw new RestApiError(400, "RESOURCE_NOT_FOUND", "Subscription not found, cannot resync."); } if (commitsFromDate && commitsFromDate.valueOf() > Date.now()) { @@ -85,7 +69,7 @@ const restSyncPost = async ( res.sendStatus(202); } catch (error: unknown) { - next(new Error("Unauthorized")); + throw new RestApiError(500, "UNKNOWN", "Something went wrong"); } }; From d07787677f05e0e669df64cd769d0f930ebc4fc4 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 15:28:30 +1100 Subject: [PATCH 12/26] chore: PR comments --- src/rest/routes/sync/index.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rest/routes/sync/index.ts b/src/rest/routes/sync/index.ts index 1aa79c0295..7131a6e06f 100644 --- a/src/rest/routes/sync/index.ts +++ b/src/rest/routes/sync/index.ts @@ -5,7 +5,7 @@ import { Subscription } from "models/subscription"; import { findOrStartSync } from "~/src/sync/sync-utils"; import { determineSyncTypeAndTargetTasks } from "~/src/util/github-sync-helper"; import { BaseLocals } from ".."; -import { InvalidArgumentError, RestApiError } from "~/src/config/errors"; +import { RestApiError } from "~/src/config/errors"; import { RestSyncReqBody } from "~/src/rest-interfaces"; @@ -45,13 +45,11 @@ const restSyncPost = async ( }, "Subscription not found when retrying sync." ); - throw new RestApiError(400, "RESOURCE_NOT_FOUND", "Subscription not found, cannot resync."); + throw new RestApiError(400, "INVALID_OR_MISSING_ARG", "Subscription not found, cannot resync."); } if (commitsFromDate && commitsFromDate.valueOf() > Date.now()) { - throw new InvalidArgumentError( - "Invalid date value, cannot select a future date!" - ); + throw new RestApiError(400, "INVALID_OR_MISSING_ARG", "Invalid date value, cannot select a future date"); } const { syncType, targetTasks } = determineSyncTypeAndTargetTasks( From 4da351c0033c16a25cb046d81840d55a1ec1dc90 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 15:58:16 +1100 Subject: [PATCH 13/26] chore: PR comments --- src/rest/routes/sync/index.test.ts | 61 +----------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/src/rest/routes/sync/index.test.ts b/src/rest/routes/sync/index.test.ts index 6515d297e6..55b748669b 100644 --- a/src/rest/routes/sync/index.test.ts +++ b/src/rest/routes/sync/index.test.ts @@ -81,7 +81,7 @@ describe("Checking the deferred request parsing route", () => { expect(resp.status).toEqual(401); }); - it("should return 200 on correct post for /rest/app/cloud/sync one for Cloud app", async () => { + it("should return 202 on correct post for /rest/app/cloud/sync one for Cloud app", async () => { return supertest(app) .post("/rest/app/cloud/sync") .set("authorization", `${getToken()}`) @@ -99,64 +99,5 @@ describe("Checking the deferred request parsing route", () => { }), expect.anything(), expect.anything()); }); }); - - it("should run incremental sync", async() => { - const commitsFromDate = new Date(new Date().getTime() - 2000); - const backfillSince = new Date(new Date().getTime() - 1000); - const subscription = await Subscription.getSingleInstallation( - jiraHost, - installationIdForServer, - gitHubServerApp.id - ); - await subscription?.update({ - syncStatus: "COMPLETE", - backfillSince - }); - return supertest(app) - .post("/rest/app/cloud/sync") - .set("authorization", `${getToken()}`) - .send({ - installationId: installationIdForServer, - jiraHost, - appId: gitHubServerApp.id, - commitsFromDate - }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - syncType: "partial", - installationId: installationIdForServer, - jiraHost, - commitsFromDate: commitsFromDate.toISOString(), - targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"], - gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) - }), expect.anything(), expect.anything()); - }); - }); - - it("should run full sync if explicitly selected by user", async () => { - const commitsFromDate = new Date(new Date().getTime() - 2000); - return supertest(app) - .post("/rest/app/cloud/sync") - .set("authorization", `${getToken()}`) - .send({ - installationId: installationIdForServer, - jiraHost, - appId: gitHubServerApp.id, - commitsFromDate, - syncType: "full" - }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - syncType: "full", - installationId: installationIdForServer, - jiraHost, - commitsFromDate: commitsFromDate.toISOString(), - targetTasks: undefined, - gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) - }), expect.anything(), expect.anything()); - }); - }); }); }); From b12fe93dfd88ab176276068450b86198283695e9 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 16:45:05 +1100 Subject: [PATCH 14/26] chore: PR comments --- src/rest/routes/sync/sync.test.ts | 155 ++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 src/rest/routes/sync/sync.test.ts diff --git a/src/rest/routes/sync/sync.test.ts b/src/rest/routes/sync/sync.test.ts new file mode 100644 index 0000000000..fa124398f5 --- /dev/null +++ b/src/rest/routes/sync/sync.test.ts @@ -0,0 +1,155 @@ +import { getFrontendApp } from "~/src/app"; +import { Installation } from "models/installation"; +import { Subscription } from "models/subscription"; +import express, { Express } from "express"; +import { RootRouter } from "routes/router"; +import supertest from "supertest"; +import { encodeSymmetric } from "atlassian-jwt"; +import { GitHubServerApp } from "models/github-server-app"; +import { v4 as newUUID } from "uuid"; +import { sqsQueues } from "~/src/sqs/queues"; + +jest.mock("~/src/sqs/queues"); +jest.mock("config/feature-flags"); + +describe("Checking the deferred request parsing route", () => { + let app: Express; + let installation: Installation; + const installationIdForCloud = 1; + const installationIdForServer = 2; + let gitHubServerApp: GitHubServerApp; + // let jwt: string; + const testSharedSecret = "test-secret"; + const clientKey = "jira-client-key"; + const getToken = ({ + secret = testSharedSecret, + iss = clientKey, + exp = Date.now() / 1000 + 10000, + qsh = "context-qsh", + sub = "myAccount" } = {}): string => { + return encodeSymmetric({ + qsh, + iss, + exp, + sub + }, secret); + }; + beforeEach(async () => { + app = getFrontendApp(); + installation = await Installation.install({ + host: jiraHost, + sharedSecret: testSharedSecret, + clientKey: clientKey + }); + await Subscription.install({ + installationId: installationIdForCloud, + host: jiraHost, + hashedClientKey: installation.clientKey, + gitHubAppId: undefined + }); + gitHubServerApp = await GitHubServerApp.install({ + uuid: newUUID(), + appId: 123, + gitHubAppName: "My GitHub Server App", + gitHubBaseUrl: gheUrl, + gitHubClientId: "lvl.1234", + gitHubClientSecret: "myghsecret", + webhookSecret: "mywebhooksecret", + privateKey: "myprivatekey", + installationId: installation.id + }, jiraHost); + await Subscription.install({ + installationId: installationIdForServer, + host: jiraHost, + hashedClientKey: installation.clientKey, + gitHubAppId: gitHubServerApp.id + }); + app = express(); + app.use(RootRouter); + }); + + describe("cloud", () => { + it("should throw 401 error when no github token is passed", async () => { + const resp = await supertest(app) + .get("/rest/app/cloud/sync"); + + expect(resp.status).toEqual(401); + }); + + it("should return 202 on correct post for /rest/app/cloud/sync one for Cloud app", async () => { + return supertest(app) + .post("/rest/app/cloud/sync") + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForCloud, + jiraHost + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + installationId: installationIdForCloud, + jiraHost, + startTime: expect.anything(), + gitHubAppConfig: expect.objectContaining({ gitHubAppId: undefined, uuid: undefined }) + }), expect.anything(), expect.anything()); + }); + }); + + it("should run incremental sync", async() => { + const commitsFromDate = new Date(new Date().getTime() - 2000); + const backfillSince = new Date(new Date().getTime() - 1000); + const subscription = await Subscription.getSingleInstallation( + jiraHost, + installationIdForServer, + gitHubServerApp.id + ); + await subscription?.update({ + syncStatus: "COMPLETE", + backfillSince + }); + return supertest(app) + .post(`/rest/app/${gitHubServerApp.id}/sync`) + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForServer, + jiraHost, + commitsFromDate + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + syncType: "partial", + installationId: installationIdForServer, + jiraHost, + commitsFromDate: commitsFromDate.toISOString(), + targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"], + gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) + }), expect.anything(), expect.anything()); + }); + }); + + it("should run full sync if explicitly selected by user", async () => { + const commitsFromDate = new Date(new Date().getTime() - 2000); + return supertest(app) + .post(`/rest/app/${gitHubServerApp.id}/sync`) + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForServer, + jiraHost, + commitsFromDate, + syncType: "full" + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + syncType: "full", + installationId: installationIdForServer, + jiraHost, + commitsFromDate: commitsFromDate.toISOString(), + targetTasks: undefined, + gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) + }), expect.anything(), expect.anything()); + }); + }); + }); +}); From 2ea4d5dce6f44829f4c3d1e387c10bd9e077da98 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 16:47:37 +1100 Subject: [PATCH 15/26] chore: PR comments --- src/rest/routes/sync/index.test.ts | 62 +++++++++++- src/rest/routes/sync/sync.test.ts | 155 ----------------------------- 2 files changed, 57 insertions(+), 160 deletions(-) delete mode 100644 src/rest/routes/sync/sync.test.ts diff --git a/src/rest/routes/sync/index.test.ts b/src/rest/routes/sync/index.test.ts index 55b748669b..fa124398f5 100644 --- a/src/rest/routes/sync/index.test.ts +++ b/src/rest/routes/sync/index.test.ts @@ -66,11 +66,6 @@ describe("Checking the deferred request parsing route", () => { }); app = express(); app.use(RootRouter); - - // jwt = encodeSymmetric({ - // qsh: "context-qsh", - // iss: "jira-client-key" - // }, await installation.decrypt("encryptedSharedSecret", getLogger("test"))); }); describe("cloud", () => { @@ -99,5 +94,62 @@ describe("Checking the deferred request parsing route", () => { }), expect.anything(), expect.anything()); }); }); + + it("should run incremental sync", async() => { + const commitsFromDate = new Date(new Date().getTime() - 2000); + const backfillSince = new Date(new Date().getTime() - 1000); + const subscription = await Subscription.getSingleInstallation( + jiraHost, + installationIdForServer, + gitHubServerApp.id + ); + await subscription?.update({ + syncStatus: "COMPLETE", + backfillSince + }); + return supertest(app) + .post(`/rest/app/${gitHubServerApp.id}/sync`) + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForServer, + jiraHost, + commitsFromDate + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + syncType: "partial", + installationId: installationIdForServer, + jiraHost, + commitsFromDate: commitsFromDate.toISOString(), + targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"], + gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) + }), expect.anything(), expect.anything()); + }); + }); + + it("should run full sync if explicitly selected by user", async () => { + const commitsFromDate = new Date(new Date().getTime() - 2000); + return supertest(app) + .post(`/rest/app/${gitHubServerApp.id}/sync`) + .set("authorization", `${getToken()}`) + .send({ + installationId: installationIdForServer, + jiraHost, + commitsFromDate, + syncType: "full" + }) + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + syncType: "full", + installationId: installationIdForServer, + jiraHost, + commitsFromDate: commitsFromDate.toISOString(), + targetTasks: undefined, + gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) + }), expect.anything(), expect.anything()); + }); + }); }); }); diff --git a/src/rest/routes/sync/sync.test.ts b/src/rest/routes/sync/sync.test.ts deleted file mode 100644 index fa124398f5..0000000000 --- a/src/rest/routes/sync/sync.test.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { getFrontendApp } from "~/src/app"; -import { Installation } from "models/installation"; -import { Subscription } from "models/subscription"; -import express, { Express } from "express"; -import { RootRouter } from "routes/router"; -import supertest from "supertest"; -import { encodeSymmetric } from "atlassian-jwt"; -import { GitHubServerApp } from "models/github-server-app"; -import { v4 as newUUID } from "uuid"; -import { sqsQueues } from "~/src/sqs/queues"; - -jest.mock("~/src/sqs/queues"); -jest.mock("config/feature-flags"); - -describe("Checking the deferred request parsing route", () => { - let app: Express; - let installation: Installation; - const installationIdForCloud = 1; - const installationIdForServer = 2; - let gitHubServerApp: GitHubServerApp; - // let jwt: string; - const testSharedSecret = "test-secret"; - const clientKey = "jira-client-key"; - const getToken = ({ - secret = testSharedSecret, - iss = clientKey, - exp = Date.now() / 1000 + 10000, - qsh = "context-qsh", - sub = "myAccount" } = {}): string => { - return encodeSymmetric({ - qsh, - iss, - exp, - sub - }, secret); - }; - beforeEach(async () => { - app = getFrontendApp(); - installation = await Installation.install({ - host: jiraHost, - sharedSecret: testSharedSecret, - clientKey: clientKey - }); - await Subscription.install({ - installationId: installationIdForCloud, - host: jiraHost, - hashedClientKey: installation.clientKey, - gitHubAppId: undefined - }); - gitHubServerApp = await GitHubServerApp.install({ - uuid: newUUID(), - appId: 123, - gitHubAppName: "My GitHub Server App", - gitHubBaseUrl: gheUrl, - gitHubClientId: "lvl.1234", - gitHubClientSecret: "myghsecret", - webhookSecret: "mywebhooksecret", - privateKey: "myprivatekey", - installationId: installation.id - }, jiraHost); - await Subscription.install({ - installationId: installationIdForServer, - host: jiraHost, - hashedClientKey: installation.clientKey, - gitHubAppId: gitHubServerApp.id - }); - app = express(); - app.use(RootRouter); - }); - - describe("cloud", () => { - it("should throw 401 error when no github token is passed", async () => { - const resp = await supertest(app) - .get("/rest/app/cloud/sync"); - - expect(resp.status).toEqual(401); - }); - - it("should return 202 on correct post for /rest/app/cloud/sync one for Cloud app", async () => { - return supertest(app) - .post("/rest/app/cloud/sync") - .set("authorization", `${getToken()}`) - .send({ - installationId: installationIdForCloud, - jiraHost - }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - installationId: installationIdForCloud, - jiraHost, - startTime: expect.anything(), - gitHubAppConfig: expect.objectContaining({ gitHubAppId: undefined, uuid: undefined }) - }), expect.anything(), expect.anything()); - }); - }); - - it("should run incremental sync", async() => { - const commitsFromDate = new Date(new Date().getTime() - 2000); - const backfillSince = new Date(new Date().getTime() - 1000); - const subscription = await Subscription.getSingleInstallation( - jiraHost, - installationIdForServer, - gitHubServerApp.id - ); - await subscription?.update({ - syncStatus: "COMPLETE", - backfillSince - }); - return supertest(app) - .post(`/rest/app/${gitHubServerApp.id}/sync`) - .set("authorization", `${getToken()}`) - .send({ - installationId: installationIdForServer, - jiraHost, - commitsFromDate - }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - syncType: "partial", - installationId: installationIdForServer, - jiraHost, - commitsFromDate: commitsFromDate.toISOString(), - targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"], - gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) - }), expect.anything(), expect.anything()); - }); - }); - - it("should run full sync if explicitly selected by user", async () => { - const commitsFromDate = new Date(new Date().getTime() - 2000); - return supertest(app) - .post(`/rest/app/${gitHubServerApp.id}/sync`) - .set("authorization", `${getToken()}`) - .send({ - installationId: installationIdForServer, - jiraHost, - commitsFromDate, - syncType: "full" - }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - syncType: "full", - installationId: installationIdForServer, - jiraHost, - commitsFromDate: commitsFromDate.toISOString(), - targetTasks: undefined, - gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) - }), expect.anything(), expect.anything()); - }); - }); - }); -}); From ae84f0000a1c666f4d26323c482f3649ce930af5 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Wed, 6 Dec 2023 22:47:00 +1100 Subject: [PATCH 16/26] chore: PR comments --- src/rest/routes/sync/index.test.ts | 60 +++++++---------------- src/rest/routes/sync/index.ts | 77 +++++++++++++++++------------- 2 files changed, 61 insertions(+), 76 deletions(-) diff --git a/src/rest/routes/sync/index.test.ts b/src/rest/routes/sync/index.test.ts index fa124398f5..90c6919155 100644 --- a/src/rest/routes/sync/index.test.ts +++ b/src/rest/routes/sync/index.test.ts @@ -82,7 +82,8 @@ describe("Checking the deferred request parsing route", () => { .set("authorization", `${getToken()}`) .send({ installationId: installationIdForCloud, - jiraHost + jiraHost, + syncType: "full" }) .expect(202) .then(() => { @@ -95,61 +96,34 @@ describe("Checking the deferred request parsing route", () => { }); }); - it("should run incremental sync", async() => { - const commitsFromDate = new Date(new Date().getTime() - 2000); - const backfillSince = new Date(new Date().getTime() - 1000); - const subscription = await Subscription.getSingleInstallation( - jiraHost, - installationIdForServer, - gitHubServerApp.id - ); - await subscription?.update({ - syncStatus: "COMPLETE", - backfillSince - }); + it("should return 400 on incorrect commitsFromDate", async () => { + const commitsFromDate = new Date(new Date().getTime() + 2000); return supertest(app) - .post(`/rest/app/${gitHubServerApp.id}/sync`) + .post("/rest/app/cloud/sync") .set("authorization", `${getToken()}`) .send({ - installationId: installationIdForServer, + installationId: installationIdForCloud, jiraHost, + syncType: "full", commitsFromDate }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - syncType: "partial", - installationId: installationIdForServer, - jiraHost, - commitsFromDate: commitsFromDate.toISOString(), - targetTasks: ["pull", "branch", "commit", "build", "deployment", "dependabotAlert", "secretScanningAlert", "codeScanningAlert"], - gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) - }), expect.anything(), expect.anything()); - }); + .expect(400); }); - it("should run full sync if explicitly selected by user", async () => { - const commitsFromDate = new Date(new Date().getTime() - 2000); + it("should return 400 on incorrect installationIdForCloud", async () => { + const commitsFromDate = new Date(new Date().getTime() + 2000); return supertest(app) - .post(`/rest/app/${gitHubServerApp.id}/sync`) + .post("/rest/app/cloud/sync") .set("authorization", `${getToken()}`) .send({ - installationId: installationIdForServer, + installationId: 11, jiraHost, - commitsFromDate, - syncType: "full" + syncType: "full", + commitsFromDate }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - syncType: "full", - installationId: installationIdForServer, - jiraHost, - commitsFromDate: commitsFromDate.toISOString(), - targetTasks: undefined, - gitHubAppConfig: expect.objectContaining({ gitHubAppId: gitHubServerApp.id, uuid: gitHubServerApp.uuid }) - }), expect.anything(), expect.anything()); - }); + .expect(400); }); + }); + }); diff --git a/src/rest/routes/sync/index.ts b/src/rest/routes/sync/index.ts index 7131a6e06f..f8a9e145b6 100644 --- a/src/rest/routes/sync/index.ts +++ b/src/rest/routes/sync/index.ts @@ -7,51 +7,60 @@ import { determineSyncTypeAndTargetTasks } from "~/src/util/github-sync-helper"; import { BaseLocals } from ".."; import { RestApiError } from "~/src/config/errors"; import { RestSyncReqBody } from "~/src/rest-interfaces"; - +// import { GitHubServerApp } from "~/src/models/github-server-app"; const restSyncPost = async ( req: Request, res: Response ) => { - //TODO: We are yet to handle enterprise backfill - const cloudOrUUID = req.params.cloudOrUUID; - const gheUUID = cloudOrUUID === "cloud" ? undefined : req.params.cloudOrUUID; - let gitHubAppId : number | undefined = undefined; - if (gheUUID) { - gitHubAppId = parseFloat(gheUUID); - } - const { installationId: gitHubInstallationId, syncType: syncTypeFromReq, - source + source, + commitsFromDate: commitsFrmDate } = req.body; + + //TODO: We are yet to handle enterprise backfill + const gitHubAppId: number | undefined = undefined; + // const cloudOrUUID = req.params.cloudOrUUID; + // const gheUUID = cloudOrUUID === "cloud" ? undefined : req.params.cloudOrUUID; + // if (gheUUID) { + // const ghEnterpriseServers: GitHubServerApp[] = await GitHubServerApp.findForInstallationId(gitHubInstallationId) || []; + // gitHubAppId = ghEnterpriseServers[0]?.appId; + // } + // A date to start fetching commit history(main and branch) from. - const commitsFromDate = req.body.commitsFromDate - ? new Date(req.body.commitsFromDate) - : undefined; + const commitsFromDate = commitsFrmDate ? new Date(commitsFrmDate) : undefined; - try { - const subscription = await Subscription.getSingleInstallation( - res.locals.installation.jiraHost, - gitHubInstallationId, - gitHubAppId + if (commitsFromDate && commitsFromDate.valueOf() > Date.now()) { + throw new RestApiError( + 400, + "INVALID_OR_MISSING_ARG", + "Invalid date value, cannot select a future date" ); - if (!subscription) { - req.log.info( - { - jiraHost: res.locals.installation.jiraHost, - installationId: gitHubInstallationId - }, - "Subscription not found when retrying sync." - ); - throw new RestApiError(400, "INVALID_OR_MISSING_ARG", "Subscription not found, cannot resync."); - } + } - if (commitsFromDate && commitsFromDate.valueOf() > Date.now()) { - throw new RestApiError(400, "INVALID_OR_MISSING_ARG", "Invalid date value, cannot select a future date"); - } + const subscription = await Subscription.getSingleInstallation( + res.locals.installation.jiraHost, + gitHubInstallationId, + gitHubAppId + ); + if (!subscription) { + req.log.info( + { + jiraHost: res.locals.installation.jiraHost, + installationId: gitHubInstallationId + }, + "Subscription not found when retrying sync." + ); + throw new RestApiError( + 400, + "INVALID_OR_MISSING_ARG", + "Subscription not found, cannot resync." + ); + } + try { const { syncType, targetTasks } = determineSyncTypeAndTargetTasks( syncTypeFromReq, subscription @@ -64,11 +73,13 @@ const restSyncPost = async ( targetTasks, { source } ); - res.sendStatus(202); } catch (error: unknown) { throw new RestApiError(500, "UNKNOWN", "Something went wrong"); } }; -export const SyncRouterHandler = errorWrapper("AnalyticsProxyHandler",restSyncPost); +export const SyncRouterHandler = errorWrapper( + "AnalyticsProxyHandler", + restSyncPost +); From 0b4d49f7a5a547f77ac07e3c31db5f5eaa762635 Mon Sep 17 00:00:00 2001 From: Kayub Maharjan Date: Thu, 7 Dec 2023 12:30:26 +1100 Subject: [PATCH 17/26] - Using the corrected datepicker --- spa/package.json | 2 +- .../Modals/RestartBackfillModal.tsx | 19 +++++---- spa/yarn.lock | 42 ++++++++++++------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/spa/package.json b/spa/package.json index 45f2ba921a..18c3da0b0e 100644 --- a/spa/package.json +++ b/spa/package.json @@ -27,7 +27,7 @@ "@atlaskit/button": "^16.8.0", "@atlaskit/checkbox": "^13.0.1", "@atlaskit/css-reset": "^6.5.2", - "@atlaskit/datetime-picker": "^13.0.2", + "@atlaskit/datetime-picker": "^13.0.3", "@atlaskit/dynamic-table": "^14.11.5", "@atlaskit/form": "^8.11.8", "@atlaskit/heading": "^1.3.7", diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx index 15d5eaeb3a..888f5ab68d 100644 --- a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx +++ b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx @@ -4,7 +4,7 @@ import Button from "@atlaskit/button"; import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; import { Checkbox } from "@atlaskit/checkbox"; import { Label } from "@atlaskit/form"; -// import { DatePicker } from "@atlaskit/datetime-picker"; +import { DatePicker } from "@atlaskit/datetime-picker"; /** * NOTE: While testing in dev mode, please disable the React.StrictMode first, @@ -15,10 +15,11 @@ const RestartBackfillModal = ({ subscription, setIsModalOpened }: { setIsModalOpened: (x: boolean) => void }) => { const [restartFromDateCheck, setRestartFromDateCheck] = useState(false); + const [backfillDate, setBackfillDate] = useState(""); const backfill = () => { // TODO: API call to disconnect this subscription - console.log("Backfill for", subscription.account.login, restartFromDateCheck); + console.log("Backfill for", subscription.account.login, restartFromDateCheck, backfillDate); setIsModalOpened(false); }; @@ -35,12 +36,14 @@ const RestartBackfillModal = ({ subscription, setIsModalOpened }: {

- {/* TODO: This datepicker is throwing a dependency issue saying module not found, add it later when fixed*/} - {/**/} +

Date: Thu, 7 Dec 2023 14:00:02 +1100 Subject: [PATCH 18/26] chore: PR comment --- spa/src/api/subscriptions/index.ts | 9 +-- spa/src/feature-flags.ts | 2 +- spa/src/pages/Connected/test.tsx | 56 -------------- spa/src/pages/Connections/index.tsx | 18 +++++ spa/src/pages/InstallationRequested/test.tsx | 35 --------- spa/src/utils/dynamicTableHelper.tsx | 1 + src/rest-interfaces/index.ts | 2 +- src/rest/rest-router.ts | 10 --- src/rest/routes/subscriptions/index.ts | 4 + .../sync.test.ts} | 48 ++++++------ .../{sync/index.ts => subscriptions/sync.ts} | 74 ++++++++++--------- 11 files changed, 95 insertions(+), 164 deletions(-) delete mode 100644 spa/src/pages/Connected/test.tsx delete mode 100644 spa/src/pages/InstallationRequested/test.tsx rename src/rest/routes/{sync/index.test.ts => subscriptions/sync.test.ts} (85%) rename src/rest/routes/{sync/index.ts => subscriptions/sync.ts} (71%) diff --git a/spa/src/api/subscriptions/index.ts b/spa/src/api/subscriptions/index.ts index fd7fe94ac0..85c894bc38 100644 --- a/spa/src/api/subscriptions/index.ts +++ b/spa/src/api/subscriptions/index.ts @@ -1,11 +1,10 @@ - -import { axiosRest, axiosRestWithGitHubToken } from "../axiosInstance"; +import { axiosRest } from "../axiosInstance"; import { RestSyncReqBody } from "~/src/rest-interfaces"; export default { getSubscriptions: () => axiosRest.get("/rest/subscriptions"), deleteSubscription: (subscriptionId: number) => - axiosRestWithGitHubToken.delete("/rest/app/cloud/subscription/:subscriptionId", { params: { subscriptionId } }), - syncSubscriptions: (data: RestSyncReqBody) => axiosRest.post(`/rest/app/cloud/sync`, data), - + axiosRest.delete(`/rest/app/cloud/subscriptions/${subscriptionId}`), + syncSubscriptions: (subscriptionId: number, reqBody: RestSyncReqBody) => + axiosRest.post(`/rest/app/cloud/subscriptions/${subscriptionId}/sync`, reqBody), }; diff --git a/spa/src/feature-flags.ts b/spa/src/feature-flags.ts index 828e3cf0ba..577bc5e23b 100644 --- a/spa/src/feature-flags.ts +++ b/spa/src/feature-flags.ts @@ -1,3 +1,3 @@ const featureFlags = () => FRONTEND_FEATURE_FLAGS ? JSON.parse(JSON.stringify(FRONTEND_FEATURE_FLAGS)) : null; -export const enableBackfillStatusPage = featureFlags()?.ENABLE_5KU_BACKFILL_PAGE; +export const enableBackfillStatusPage = featureFlags()?.ENABLE_5KU_BACKFILL_PAGE || true; diff --git a/spa/src/pages/Connected/test.tsx b/spa/src/pages/Connected/test.tsx deleted file mode 100644 index 82e270b4d0..0000000000 --- a/spa/src/pages/Connected/test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { BrowserRouter } from "react-router-dom"; -import { act, render, screen } from "@testing-library/react"; -import Connected from "./index"; -import userEvent from "@testing-library/user-event"; - -// Mocking the global variable -/* eslint-disable @typescript-eslint/no-explicit-any*/ -(global as any).AP = { - navigator: { - go: jest.fn() - } -}; -(global as any).open = jest.fn(); - -const navigate = jest.fn(); -jest.mock("react-router-dom", () => ({ - ...jest.requireActual("react-router-dom"), - useNavigate: () => navigate, - useLocation: jest.fn().mockReturnValue({ - state: { orgLogin: "AtlassianOrg" }, - }), -})); -jest.mock("./../../feature-flags", () => ({ - enableBackfillStatusPage: false -})); - -jest.mock("../../analytics/analytics-proxy-client", () => { - return { - analyticsProxyClient: { - sendScreenEvent: jest.fn(), - sendUIEvent: jest.fn() - } - }; -}); - -test("Basic check for the Connected Page", async () => { - render( - - - - ); - - expect(screen.queryByText("AtlassianOrg is now connected!")).toBeInTheDocument(); - expect(screen.queryByText("Add another organization")).toBeInTheDocument(); - expect(screen.queryByText("How to add issue keys")).toBeInTheDocument(); - expect(screen.queryByText("Exit set up")).toBeInTheDocument(); - - await userEvent.click(screen.getByText("How to add issue keys")); - expect(window.open).toBeCalledWith("https://support.atlassian.com/jira-software-cloud/docs/reference-issues-in-your-development-work/", "_blank"); - - await userEvent.click(screen.getByText("Exit set up")); - expect(AP.navigator.go).toHaveBeenCalled(); - - await act(() => userEvent.click(screen.getByText("Add another organization"))); - expect(navigate).toHaveBeenCalledWith("/spa/steps"); -}); diff --git a/spa/src/pages/Connections/index.tsx b/spa/src/pages/Connections/index.tsx index 905091c9b3..cbb378b723 100644 --- a/spa/src/pages/Connections/index.tsx +++ b/spa/src/pages/Connections/index.tsx @@ -9,6 +9,7 @@ import { GHSubscriptions } from "../../rest-interfaces"; import { reportError } from "../../utils"; import SkeletonForLoading from "./SkeletonForLoading"; import { useNavigate } from "react-router-dom"; +import Button from "@atlaskit/button"; const Connections = () => { const navigate = useNavigate(); @@ -25,6 +26,22 @@ const Connections = () => { setIsLoading(false); } }; + + const syncGHSubscriptions = async () => { + try { + setIsLoading(true); + await ApiRequest.subscriptions.syncSubscriptions(4,{ + commitsFromDate: "2021-12-03", + syncType: "full", + source: "backfill-button", + }); + } catch (e) { + reportError(e, { path: "Fetching subscriptions" }); + } finally { + setIsLoading(false); + } + }; + useEffect(() => { fetchGHSubscriptions(); }, []); @@ -39,6 +56,7 @@ const Connections = () => { return ( + { isLoading ? : <> { diff --git a/spa/src/pages/InstallationRequested/test.tsx b/spa/src/pages/InstallationRequested/test.tsx deleted file mode 100644 index 27247e9b6a..0000000000 --- a/spa/src/pages/InstallationRequested/test.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { BrowserRouter } from "react-router-dom"; -import { act, render, screen } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import InstallationRequested from "./index"; -import OAuthManager from "../../services/oauth-manager"; - -const navigate = jest.fn(); -jest.mock("react-router-dom", () => ({ - ...jest.requireActual("react-router-dom"), - useNavigate: () => navigate -})); -jest.mock("../../services/oauth-manager"); -window.open = jest.fn(); - -test("Basic check for the Connected Page", async () => { - jest.mocked(OAuthManager).getUserDetails = jest.fn().mockReturnValue({ username: "kay" }); - jest.mocked(OAuthManager).clear = jest.fn(); - - render( - - - - ); - - expect(screen.queryByText("Once the owner of this organization has installed Jira, you (or another Jira admin) can come back here and finish the set up.")).toBeInTheDocument(); - expect(screen.queryByText("Add another organization")).toBeInTheDocument(); - expect(screen.queryByText("Change GitHub login")).toBeInTheDocument(); - - await act(() => userEvent.click(screen.getByText("Add another organization"))); - expect(navigate).toHaveBeenCalledWith("/spa/steps"); - - await act(() => userEvent.click(screen.getByText("Change GitHub login"))); - expect(window.open).toHaveBeenCalled(); - expect(OAuthManager.clear).toHaveBeenCalled(); -}); diff --git a/spa/src/utils/dynamicTableHelper.tsx b/spa/src/utils/dynamicTableHelper.tsx index 96dac29f03..4e411f391a 100644 --- a/spa/src/utils/dynamicTableHelper.tsx +++ b/spa/src/utils/dynamicTableHelper.tsx @@ -106,6 +106,7 @@ export const getGHSubscriptionsRows = ( src={cloudConnection.account.avatar_url} size="medium" /> + {cloudConnection.subscriptionId} )} diff --git a/src/rest-interfaces/index.ts b/src/rest-interfaces/index.ts index 6d015f6bde..9ec5151707 100644 --- a/src/rest-interfaces/index.ts +++ b/src/rest-interfaces/index.ts @@ -1,5 +1,4 @@ export type RestSyncReqBody = { - installationId: number; syncType: string; source: string; commitsFromDate: string; @@ -124,6 +123,7 @@ export type SuccessfulConnection = { jiraHost: string; isGlobalInstall: boolean; backfillSince: string | null; + subscriptionId: number; }; export type FailedCloudConnection = { diff --git a/src/rest/rest-router.ts b/src/rest/rest-router.ts index 4ac784c776..a0df75f6a5 100644 --- a/src/rest/rest-router.ts +++ b/src/rest/rest-router.ts @@ -1,6 +1,5 @@ import { Router } from "express"; import { JwtHandler } from "./middleware/jwt/jwt-handler"; -import { body } from "express-validator"; import { OAuthRouter } from "./routes/oauth"; import { OAuthCallbackHandler, OrgsInstalledHandler, OrgsInstallRequestedHandler } from "./routes/github-callback"; import { GitHubOrgsRouter } from "./routes/github-orgs"; @@ -11,7 +10,6 @@ import { RestErrorHandler } from "./middleware/error"; import { JiraAdminEnforceMiddleware } from "./middleware/jira-admin/jira-admin-check"; import { AnalyticsProxyHandler } from "./routes/analytics-proxy"; import { SubscriptionsRouter } from "./routes/subscriptions"; -import { SyncRouterHandler } from "./routes/sync"; import { DeferredRouter } from "./routes/deferred"; export const RestRouter = Router({ mergeParams: true }); @@ -29,14 +27,6 @@ RestRouter.use("/subscriptions", JwtHandler, JiraAdminEnforceMiddleware, Subscri */ RestRouter.use("/app/:cloudOrUUID", subRouter); -subRouter.post( - "/sync", - body("commitsFromDate").optional().isISO8601(), - JwtHandler, - JiraAdminEnforceMiddleware, - SyncRouterHandler -); - subRouter.get("/github-callback", OAuthCallbackHandler); subRouter.get("/github-installed", OrgsInstalledHandler); subRouter.get("/github-requested", OrgsInstallRequestedHandler); diff --git a/src/rest/routes/subscriptions/index.ts b/src/rest/routes/subscriptions/index.ts index c947b41282..2f852879f4 100644 --- a/src/rest/routes/subscriptions/index.ts +++ b/src/rest/routes/subscriptions/index.ts @@ -5,6 +5,7 @@ import { Installation } from "models/installation"; import { removeSubscription } from "utils/jira-utils"; import { GitHubServerApp } from "models/github-server-app"; import { InvalidArgumentError } from "config/errors"; +import { SyncRouterHandler } from "./sync"; export const SubscriptionsRouter = Router({ mergeParams: true }); @@ -34,7 +35,10 @@ SubscriptionsRouter.delete("/", errorWrapper("SubscriptionDelete", async (req: R const gitHubAppId = cloudOrUUID === "cloud" ? undefined : (await GitHubServerApp.getForUuidAndInstallationId(cloudOrUUID, installation.id))?.appId; //TODO: validate the uuid regex + // eslint-disable-next-line @typescript-eslint/no-unsafe-call await removeSubscription(installation, undefined, gitHubAppId, req.log, subscriptionId); res.sendStatus(204); })); + +SubscriptionsRouter.post("/sync", SyncRouterHandler); \ No newline at end of file diff --git a/src/rest/routes/sync/index.test.ts b/src/rest/routes/subscriptions/sync.test.ts similarity index 85% rename from src/rest/routes/sync/index.test.ts rename to src/rest/routes/subscriptions/sync.test.ts index 90c6919155..68269bf076 100644 --- a/src/rest/routes/sync/index.test.ts +++ b/src/rest/routes/subscriptions/sync.test.ts @@ -17,6 +17,8 @@ describe("Checking the deferred request parsing route", () => { let installation: Installation; const installationIdForCloud = 1; const installationIdForServer = 2; + const gitHubInstallationId = 15; + let subscription; let gitHubServerApp: GitHubServerApp; // let jwt: string; const testSharedSecret = "test-secret"; @@ -66,43 +68,39 @@ describe("Checking the deferred request parsing route", () => { }); app = express(); app.use(RootRouter); + subscription = await Subscription.create({ + gitHubInstallationId, + jiraHost + }); }); describe("cloud", () => { it("should throw 401 error when no github token is passed", async () => { const resp = await supertest(app) - .get("/rest/app/cloud/sync"); + .get(`/rest/app/cloud/subscriptions/${subscription.id}/sync`); expect(resp.status).toEqual(401); }); - it("should return 202 on correct post for /rest/app/cloud/sync one for Cloud app", async () => { + it("should return 400 on incorrect commitsFromDate", async () => { + const commitsFromDate = new Date(new Date().getTime() - 2000); return supertest(app) - .post("/rest/app/cloud/sync") + .post(`/rest/app/cloud/subscriptions/${undefined}/sync`) .set("authorization", `${getToken()}`) .send({ - installationId: installationIdForCloud, jiraHost, - syncType: "full" + syncType: "full", + commitsFromDate }) - .expect(202) - .then(() => { - expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ - installationId: installationIdForCloud, - jiraHost, - startTime: expect.anything(), - gitHubAppConfig: expect.objectContaining({ gitHubAppId: undefined, uuid: undefined }) - }), expect.anything(), expect.anything()); - }); + .expect(400); }); - it("should return 400 on incorrect commitsFromDate", async () => { + it("should return 400 on incorrect installationIdForCloud", async () => { const commitsFromDate = new Date(new Date().getTime() + 2000); return supertest(app) - .post("/rest/app/cloud/sync") + .post(`/rest/app/cloud/subscriptions/${subscription.id}/sync`) .set("authorization", `${getToken()}`) .send({ - installationId: installationIdForCloud, jiraHost, syncType: "full", commitsFromDate @@ -110,18 +108,24 @@ describe("Checking the deferred request parsing route", () => { .expect(400); }); - it("should return 400 on incorrect installationIdForCloud", async () => { - const commitsFromDate = new Date(new Date().getTime() + 2000); + it("should return 202 on correct post for /rest/app/cloud/sync one for Cloud app", async () => { + const commitsFromDate = new Date(new Date().getTime() - 2000); return supertest(app) - .post("/rest/app/cloud/sync") + .post(`/rest/app/cloud/subscriptions/${subscription.id}/sync`) .set("authorization", `${getToken()}`) .send({ - installationId: 11, jiraHost, syncType: "full", commitsFromDate }) - .expect(400); + .expect(202) + .then(() => { + expect(sqsQueues.backfill.sendMessage).toBeCalledWith(expect.objectContaining({ + jiraHost, + startTime: expect.anything(), + gitHubAppConfig: expect.objectContaining({ gitHubAppId: undefined, uuid: undefined }) + }), expect.anything(), expect.anything()); + }); }); }); diff --git a/src/rest/routes/sync/index.ts b/src/rest/routes/subscriptions/sync.ts similarity index 71% rename from src/rest/routes/sync/index.ts rename to src/rest/routes/subscriptions/sync.ts index f8a9e145b6..372b7494fb 100644 --- a/src/rest/routes/sync/index.ts +++ b/src/rest/routes/subscriptions/sync.ts @@ -14,24 +14,13 @@ const restSyncPost = async ( res: Response ) => { const { - installationId: gitHubInstallationId, syncType: syncTypeFromReq, source, commitsFromDate: commitsFrmDate } = req.body; - //TODO: We are yet to handle enterprise backfill - const gitHubAppId: number | undefined = undefined; - // const cloudOrUUID = req.params.cloudOrUUID; - // const gheUUID = cloudOrUUID === "cloud" ? undefined : req.params.cloudOrUUID; - // if (gheUUID) { - // const ghEnterpriseServers: GitHubServerApp[] = await GitHubServerApp.findForInstallationId(gitHubInstallationId) || []; - // gitHubAppId = ghEnterpriseServers[0]?.appId; - // } - // A date to start fetching commit history(main and branch) from. const commitsFromDate = commitsFrmDate ? new Date(commitsFrmDate) : undefined; - if (commitsFromDate && commitsFromDate.valueOf() > Date.now()) { throw new RestApiError( 400, @@ -40,17 +29,37 @@ const restSyncPost = async ( ); } - const subscription = await Subscription.getSingleInstallation( - res.locals.installation.jiraHost, - gitHubInstallationId, - gitHubAppId - ); + const subscriptionId: number = Number(req.params.subscriptionId); + if (!subscriptionId) { + req.log.info( + { + jiraHost: res.locals.installation.jiraHost, + subscriptionId + }, + "Subscription ID not found when retrying sync." + ); + throw new RestApiError( + 400, + "INVALID_OR_MISSING_ARG", + "Subscription ID not found when retrying sync." + ); + } + //TODO: We are yet to handle enterprise backfill + // const gitHubAppId: number | undefined = undefined; + // const cloudOrUUID = req.params.cloudOrUUID; + // const gheUUID = cloudOrUUID === "cloud" ? undefined : req.params.cloudOrUUID; + // if (gheUUID) { + // const ghEnterpriseServers: GitHubServerApp[] = await GitHubServerApp.findForInstallationId(gitHubInstallationId) || []; + // gitHubAppId = ghEnterpriseServers[0]?.appId; + // } + + const subscription = await Subscription.findByPk(subscriptionId); if (!subscription) { req.log.info( { jiraHost: res.locals.installation.jiraHost, - installationId: gitHubInstallationId + subscriptionId }, "Subscription not found when retrying sync." ); @@ -60,23 +69,20 @@ const restSyncPost = async ( "Subscription not found, cannot resync." ); } - try { - const { syncType, targetTasks } = determineSyncTypeAndTargetTasks( - syncTypeFromReq, - subscription - ); - await findOrStartSync( - subscription, - req.log, - syncType, - commitsFromDate || subscription.backfillSince, - targetTasks, - { source } - ); - res.sendStatus(202); - } catch (error: unknown) { - throw new RestApiError(500, "UNKNOWN", "Something went wrong"); - } + + const { syncType, targetTasks } = determineSyncTypeAndTargetTasks( + syncTypeFromReq, + subscription + ); + await findOrStartSync( + subscription, + req.log, + syncType, + commitsFromDate || subscription.backfillSince, + targetTasks, + { source } + ); + res.sendStatus(202); }; export const SyncRouterHandler = errorWrapper( From 263b5dc557ad187e31f45cc426f82448df782a52 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 14:36:18 +1100 Subject: [PATCH 19/26] chore: revoke unnecessary changes --- spa/package.json | 2 - spa/src/feature-flags.ts | 2 +- spa/src/pages/Connected/index.tsx | 2 +- spa/src/pages/Connected/test.tsx | 56 ++++ .../Connections/GHCloudConnections/index.tsx | 61 +--- .../Modals/DisconnectSubscriptionModal.tsx | 44 --- .../Modals/RestartBackfillModal.tsx | 67 ---- spa/src/pages/Connections/index.tsx | 20 +- spa/src/pages/InstallationRequested/test.tsx | 35 ++ spa/src/utils/dynamicTableHelper.tsx | 45 +-- spa/yarn.lock | 303 +----------------- test/snapshots/app.test.ts.snap | 6 +- 12 files changed, 114 insertions(+), 529 deletions(-) create mode 100644 spa/src/pages/Connected/test.tsx delete mode 100644 spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx delete mode 100644 spa/src/pages/Connections/Modals/RestartBackfillModal.tsx create mode 100644 spa/src/pages/InstallationRequested/test.tsx diff --git a/spa/package.json b/spa/package.json index 18c3da0b0e..46bce986a2 100644 --- a/spa/package.json +++ b/spa/package.json @@ -25,9 +25,7 @@ "@atlaskit/avatar": "^21.3.9", "@atlaskit/badge": "^15.1.14", "@atlaskit/button": "^16.8.0", - "@atlaskit/checkbox": "^13.0.1", "@atlaskit/css-reset": "^6.5.2", - "@atlaskit/datetime-picker": "^13.0.3", "@atlaskit/dynamic-table": "^14.11.5", "@atlaskit/form": "^8.11.8", "@atlaskit/heading": "^1.3.7", diff --git a/spa/src/feature-flags.ts b/spa/src/feature-flags.ts index 577bc5e23b..828e3cf0ba 100644 --- a/spa/src/feature-flags.ts +++ b/spa/src/feature-flags.ts @@ -1,3 +1,3 @@ const featureFlags = () => FRONTEND_FEATURE_FLAGS ? JSON.parse(JSON.stringify(FRONTEND_FEATURE_FLAGS)) : null; -export const enableBackfillStatusPage = featureFlags()?.ENABLE_5KU_BACKFILL_PAGE || true; +export const enableBackfillStatusPage = featureFlags()?.ENABLE_5KU_BACKFILL_PAGE; diff --git a/spa/src/pages/Connected/index.tsx b/spa/src/pages/Connected/index.tsx index a004977c3f..5145f9cef0 100644 --- a/spa/src/pages/Connected/index.tsx +++ b/spa/src/pages/Connected/index.tsx @@ -150,4 +150,4 @@ const Connected = () => { ); }; -export default Connected; +export default Connected; \ No newline at end of file diff --git a/spa/src/pages/Connected/test.tsx b/spa/src/pages/Connected/test.tsx new file mode 100644 index 0000000000..1192493d07 --- /dev/null +++ b/spa/src/pages/Connected/test.tsx @@ -0,0 +1,56 @@ +import { BrowserRouter } from "react-router-dom"; +import { act, render, screen } from "@testing-library/react"; +import Connected from "./index"; +import userEvent from "@testing-library/user-event"; + +// Mocking the global variable +/* eslint-disable @typescript-eslint/no-explicit-any*/ +(global as any).AP = { + navigator: { + go: jest.fn() + } +}; +(global as any).open = jest.fn(); + +const navigate = jest.fn(); +jest.mock("react-router-dom", () => ({ + ...jest.requireActual("react-router-dom"), + useNavigate: () => navigate, + useLocation: jest.fn().mockReturnValue({ + state: { orgLogin: "AtlassianOrg" }, + }), +})); +jest.mock("./../../feature-flags", () => ({ + enableBackfillStatusPage: false +})); + +jest.mock("../../analytics/analytics-proxy-client", () => { + return { + analyticsProxyClient: { + sendScreenEvent: jest.fn(), + sendUIEvent: jest.fn() + } + }; +}); + +test("Basic check for the Connected Page", async () => { + render( + + + + ); + + expect(screen.queryByText("AtlassianOrg is now connected!")).toBeInTheDocument(); + expect(screen.queryByText("Add another organization")).toBeInTheDocument(); + expect(screen.queryByText("How to add issue keys")).toBeInTheDocument(); + expect(screen.queryByText("Exit set up")).toBeInTheDocument(); + + await userEvent.click(screen.getByText("How to add issue keys")); + expect(window.open).toBeCalledWith("https://support.atlassian.com/jira-software-cloud/docs/reference-issues-in-your-development-work/", "_blank"); + + await userEvent.click(screen.getByText("Exit set up")); + expect(AP.navigator.go).toHaveBeenCalled(); + + await act(() => userEvent.click(screen.getByText("Add another organization"))); + expect(navigate).toHaveBeenCalledWith("/spa/steps"); +}); \ No newline at end of file diff --git a/spa/src/pages/Connections/GHCloudConnections/index.tsx b/spa/src/pages/Connections/GHCloudConnections/index.tsx index 1983b34420..6f60cafd28 100644 --- a/spa/src/pages/Connections/GHCloudConnections/index.tsx +++ b/spa/src/pages/Connections/GHCloudConnections/index.tsx @@ -1,16 +1,11 @@ /** @jsxImportSource @emotion/react */ -import { useState } from "react"; import { DynamicTableStateless } from "@atlaskit/dynamic-table"; import { head, getGHSubscriptionsRows, } from "../../../utils/dynamicTableHelper"; -import { BackfillPageModalTypes, GhCloudSubscriptions } from "../../../rest-interfaces"; +import { GhCloudSubscriptions } from "../../../rest-interfaces"; import { Box, xcss } from "@atlaskit/primitives"; -import { SuccessfulConnection } from "rest-interfaces"; -import DisconnectSubscriptionModal from "../Modals/DisconnectSubscriptionModal"; -import RestartBackfillModal from "../Modals/RestartBackfillModal"; -import { ModalTransition } from "@atlaskit/modal-dialog"; const containerStyles = xcss({ display: "flex", @@ -20,55 +15,19 @@ const containerStyles = xcss({ type GitHubCloudConnectionsProps = { ghCloudSubscriptions: GhCloudSubscriptions; }; - const GitHubCloudConnections = ({ ghCloudSubscriptions, }: GitHubCloudConnectionsProps) => { - const [isModalOpened, setIsModalOpened] = useState(false); - const [subscriptionForModal, setSubscriptionForModal] = useState(undefined); - const [selectedModal, setSelectedModal] = useState("BACKFILL"); - - const openedModal = () => { - switch (selectedModal) { - case "BACKFILL": - return (); - case "DISCONNECT_SUBSCRIPTION": - return ; - // TODO: Create modals for GHE later - case "DISCONNECT_SERVER": - case "DISCONNECT_SERVER_APP": - default: - return <>; - } - }; - return ( - <> - - - - - - { - isModalOpened && subscriptionForModal && openedModal() - } - - + + + ); }; -export default GitHubCloudConnections; +export default GitHubCloudConnections; \ No newline at end of file diff --git a/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx b/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx deleted file mode 100644 index a5ddf794f8..0000000000 --- a/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from "@atlaskit/modal-dialog"; -import Button from "@atlaskit/button"; -import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; - -/** - * NOTE: While testing in dev mode, please disable the React.StrictMode first, - * otherwise this modal won't show up. - */ -const DisconnectSubscriptionModal = ({ subscription, setIsModalOpened }: { - subscription: SuccessfulConnection, - setIsModalOpened: (x: boolean) => void -}) => { - const disconnect = () => { - // TODO: API call to disconnect this subscription - console.log("Disconnect", subscription.account.login); - setIsModalOpened(false); - }; - - return ( - <> - setIsModalOpened(false)}> - - - <>Disconnect {subscription.account.login}? - - - -

- Are you sure you want to disconnect your organization {subscription.account.login}? - This means that you will have to redo the backfill of historical data if you ever want to reconnect -

-
- - - - -
- - ); -}; - -export default DisconnectSubscriptionModal; diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx deleted file mode 100644 index 888f5ab68d..0000000000 --- a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useState } from "react"; -import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from "@atlaskit/modal-dialog"; -import Button from "@atlaskit/button"; -import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; -import { Checkbox } from "@atlaskit/checkbox"; -import { Label } from "@atlaskit/form"; -import { DatePicker } from "@atlaskit/datetime-picker"; - -/** - * NOTE: While testing in dev mode, please disable the React.StrictMode first, - * otherwise this modal won't show up. - */ -const RestartBackfillModal = ({ subscription, setIsModalOpened }: { - subscription: SuccessfulConnection, - setIsModalOpened: (x: boolean) => void -}) => { - const [restartFromDateCheck, setRestartFromDateCheck] = useState(false); - const [backfillDate, setBackfillDate] = useState(""); - - const backfill = () => { - // TODO: API call to disconnect this subscription - console.log("Backfill for", subscription.account.login, restartFromDateCheck, backfillDate); - setIsModalOpened(false); - }; - - return ( - <> - setIsModalOpened(false)}> - - Backfill your data - - -

- Backfilling data can take a long time, so we’ll only backfill your data from the last 6 months. - If you want to backfill more data, choose a date below. Branches will be backfilled regardless of their age. -

-

- - -

-

- setRestartFromDateCheck(!restartFromDateCheck)} - label={`Restart the backfill from today to this date`} - name="restart-from-selected-date" - /> -

-
- - - - -
- - ); -}; - -export default RestartBackfillModal; diff --git a/spa/src/pages/Connections/index.tsx b/spa/src/pages/Connections/index.tsx index cbb378b723..35832397ae 100644 --- a/spa/src/pages/Connections/index.tsx +++ b/spa/src/pages/Connections/index.tsx @@ -9,7 +9,6 @@ import { GHSubscriptions } from "../../rest-interfaces"; import { reportError } from "../../utils"; import SkeletonForLoading from "./SkeletonForLoading"; import { useNavigate } from "react-router-dom"; -import Button from "@atlaskit/button"; const Connections = () => { const navigate = useNavigate(); @@ -26,22 +25,6 @@ const Connections = () => { setIsLoading(false); } }; - - const syncGHSubscriptions = async () => { - try { - setIsLoading(true); - await ApiRequest.subscriptions.syncSubscriptions(4,{ - commitsFromDate: "2021-12-03", - syncType: "full", - source: "backfill-button", - }); - } catch (e) { - reportError(e, { path: "Fetching subscriptions" }); - } finally { - setIsLoading(false); - } - }; - useEffect(() => { fetchGHSubscriptions(); }, []); @@ -56,7 +39,6 @@ const Connections = () => { return ( - { isLoading ? : <> { @@ -76,4 +58,4 @@ const Connections = () => { ); }; -export default Connections; +export default Connections; \ No newline at end of file diff --git a/spa/src/pages/InstallationRequested/test.tsx b/spa/src/pages/InstallationRequested/test.tsx new file mode 100644 index 0000000000..27247e9b6a --- /dev/null +++ b/spa/src/pages/InstallationRequested/test.tsx @@ -0,0 +1,35 @@ +import { BrowserRouter } from "react-router-dom"; +import { act, render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import InstallationRequested from "./index"; +import OAuthManager from "../../services/oauth-manager"; + +const navigate = jest.fn(); +jest.mock("react-router-dom", () => ({ + ...jest.requireActual("react-router-dom"), + useNavigate: () => navigate +})); +jest.mock("../../services/oauth-manager"); +window.open = jest.fn(); + +test("Basic check for the Connected Page", async () => { + jest.mocked(OAuthManager).getUserDetails = jest.fn().mockReturnValue({ username: "kay" }); + jest.mocked(OAuthManager).clear = jest.fn(); + + render( + + + + ); + + expect(screen.queryByText("Once the owner of this organization has installed Jira, you (or another Jira admin) can come back here and finish the set up.")).toBeInTheDocument(); + expect(screen.queryByText("Add another organization")).toBeInTheDocument(); + expect(screen.queryByText("Change GitHub login")).toBeInTheDocument(); + + await act(() => userEvent.click(screen.getByText("Add another organization"))); + expect(navigate).toHaveBeenCalledWith("/spa/steps"); + + await act(() => userEvent.click(screen.getByText("Change GitHub login"))); + expect(window.open).toHaveBeenCalled(); + expect(OAuthManager.clear).toHaveBeenCalled(); +}); diff --git a/spa/src/utils/dynamicTableHelper.tsx b/spa/src/utils/dynamicTableHelper.tsx index 4e411f391a..14a30b67e8 100644 --- a/spa/src/utils/dynamicTableHelper.tsx +++ b/spa/src/utils/dynamicTableHelper.tsx @@ -3,7 +3,7 @@ import Avatar from "@atlaskit/avatar"; import Badge from "@atlaskit/badge"; import { token } from "@atlaskit/tokens"; import Lozenge from "@atlaskit/lozenge"; -import { BackfillPageModalTypes, SuccessfulConnection } from "../rest-interfaces"; +import { SuccessfulConnection } from "../rest-interfaces"; import { ThemeAppearance } from "@atlaskit/lozenge/dist/types/Lozenge"; import { css } from "@emotion/react"; @@ -13,12 +13,6 @@ type Row = { cells: { key: string | number; content: React.JSX.Element | string | number }[]; }; -type ConnectionsActionsCallback = { - setIsModalOpened: (x: boolean) => void; - setSubscriptionForModal: (sub: SuccessfulConnection) => void; - setSelectedModal: (x: BackfillPageModalTypes) => void; -}; - const rowWrapperStyle = css` display: flex; align-items: center; @@ -63,19 +57,14 @@ const createHead = (withWidth: boolean) => { width: withWidth ? 30 : undefined, }, { - key: "repos", + key: "party", content: "Repos", width: withWidth ? 30 : undefined, }, { - key: "status", + key: "term", content: "Status", width: withWidth ? 30 : undefined, - }, - { - key: "options", - content: "Settings", - width: withWidth ? 10: undefined, } ], }; @@ -84,8 +73,7 @@ const createHead = (withWidth: boolean) => { export const head = createHead(true); export const getGHSubscriptionsRows = ( - SuccessfulConnections: SuccessfulConnection[], - callbacks?: ConnectionsActionsCallback + SuccessfulConnections: SuccessfulConnection[] ): Row[] => { if (!SuccessfulConnections) { return []; @@ -106,7 +94,6 @@ export const getGHSubscriptionsRows = ( src={cloudConnection.account.avatar_url} size="medium" /> - {cloudConnection.subscriptionId} )} @@ -160,30 +147,8 @@ export const getGHSubscriptionsRows = ( ), - }, - { - key: cloudConnection.id, - content: ( - <> - {/* TODO: Convert this into a dropdown */} -
{ - callbacks?.setIsModalOpened(true); - callbacks?.setSubscriptionForModal(cloudConnection); - callbacks?.setSelectedModal("DISCONNECT_SUBSCRIPTION"); - }}> - Disconnect -
-
{ - callbacks?.setIsModalOpened(true); - callbacks?.setSubscriptionForModal(cloudConnection); - callbacks?.setSelectedModal("BACKFILL"); - }}> - Backfill -
- - ) } ], }) ); -}; +}; \ No newline at end of file diff --git a/spa/yarn.lock b/spa/yarn.lock index 26f4f36526..09b7ab384a 100644 --- a/spa/yarn.lock +++ b/spa/yarn.lock @@ -51,15 +51,6 @@ prop-types "^15.5.10" use-memo-one "^1.1.1" -"@atlaskit/app-provider@^0.4.0": - version "0.4.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/app-provider/-/app-provider-0.4.0.tgz#33d9706bd81a8799b041ab36ecb2537f1b57b09c" - integrity sha512-ZvDVRSHD19rxKvx+YomWvekvtPzNbZEwYdHGXWG3QjdnP+1oBEeaVgkI9LnpWAoreunoQ8xUgmJ6g2qAYBjnoA== - dependencies: - "@atlaskit/tokens" "^1.28.0" - "@babel/runtime" "^7.0.0" - bind-event-listener "^2.1.1" - "@atlaskit/avatar@^21.3.9": version "21.3.9" resolved "https://registry.yarnpkg.com/@atlaskit/avatar/-/avatar-21.3.9.tgz#01049b9730d0374a4352728bc08aba638e0011e6" @@ -96,25 +87,6 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" -"@atlaskit/button@^16.17.0": - version "16.17.11" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/button/-/button-16.17.11.tgz#2b4410b28ea7a627f529e37a50e9fb13aebddbb7" - integrity sha512-1zwdb6saW8XTf//8CQ24sEACgQqzG++bJvz1mqQo2t+embRo+OWgOgTYf4MGzkztGw9vIOMdreiiLAv9NNCpvA== - dependencies: - "@atlaskit/analytics-next" "^9.1.0" - "@atlaskit/ds-lib" "^2.2.0" - "@atlaskit/focus-ring" "^1.3.0" - "@atlaskit/icon" "^22.0.0" - "@atlaskit/interaction-context" "^2.1.0" - "@atlaskit/platform-feature-flags" "^0.2.0" - "@atlaskit/primitives" "^1.13.0" - "@atlaskit/spinner" "^16.0.0" - "@atlaskit/theme" "^12.6.0" - "@atlaskit/tokens" "^1.29.0" - "@atlaskit/visually-hidden" "^1.2.4" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - "@atlaskit/button@^16.8.0": version "16.8.0" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/button/-/button-16.8.0.tgz#85afd61b219f4a6b25524e61f9ca3da769a438b7" @@ -148,40 +120,6 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" -"@atlaskit/calendar@^14.0.0": - version "14.0.3" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/calendar/-/calendar-14.0.3.tgz#4e27ed33457958eec024af98512c957b77f50348" - integrity sha512-+mSUBALjCd3Qk4VwHmPvO6Y0KdblzvQSF5TAaRPRjMwoDw3V/i3USOWcTQ1obFSXm327ZulW+uLqTM3YsklvDA== - dependencies: - "@atlaskit/analytics-next" "^9.1.0" - "@atlaskit/button" "^16.17.0" - "@atlaskit/ds-explorations" "^3.0.0" - "@atlaskit/ds-lib" "^2.2.0" - "@atlaskit/heading" "^1.4.0" - "@atlaskit/icon" "^22.0.0" - "@atlaskit/locale" "^2.6.0" - "@atlaskit/primitives" "^1.13.0" - "@atlaskit/theme" "^12.6.0" - "@atlaskit/tokens" "^1.29.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - date-fns "^2.17.0" - react-uid "^2.2.0" - -"@atlaskit/checkbox@^13.0.1": - version "13.0.1" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/checkbox/-/checkbox-13.0.1.tgz#316ed8382a6fcee66f60a676c96f24bbc6af67af" - integrity sha512-/KkaC02GnG4XoQ93Zvy5TbfDGFN2VyD00VuHg8s/y9hPuzEbFxdh6GVB/6IYMXqQ5xRkJb22X7W5cw7miCkTuA== - dependencies: - "@atlaskit/analytics-next" "^9.1.0" - "@atlaskit/ds-lib" "^2.2.0" - "@atlaskit/icon" "^22.0.0" - "@atlaskit/platform-feature-flags" "^0.2.0" - "@atlaskit/theme" "^12.6.0" - "@atlaskit/tokens" "^1.28.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - "@atlaskit/codemod-utils@^4.2.0": version "4.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/codemod-utils/-/codemod-utils-4.2.3.tgz#3f217fb7e6ad9f638ab03bdaa10d2704d2a26952" @@ -199,28 +137,6 @@ "@babel/runtime" "^7.0.0" fbjs "^3.0.0" -"@atlaskit/datetime-picker@^13.0.3": - version "13.0.3" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/datetime-picker/-/datetime-picker-13.0.3.tgz#36ed3ac39e2baac5e69580ee197be70c9e26fc49" - integrity sha512-xWo7faLWzOnNbpk1HcpkD0XyryPhmIWwA+ee4xSjIBnHhxTJAbUTH0CdpeYJ6sZqnm2HjmVdKDmMl6cDo61xpg== - dependencies: - "@atlaskit/analytics-next" "^9.1.0" - "@atlaskit/calendar" "^14.0.0" - "@atlaskit/ds-lib" "^2.2.0" - "@atlaskit/icon" "^22.0.0" - "@atlaskit/layering" "^0.2.0" - "@atlaskit/locale" "^2.6.0" - "@atlaskit/platform-feature-flags" "^0.2.0" - "@atlaskit/popper" "^5.5.0" - "@atlaskit/select" "^17.0.0" - "@atlaskit/theme" "^12.6.0" - "@atlaskit/tokens" "^1.29.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - date-fns "^2.17.0" - lodash "^4.17.21" - react-scrolllock "^5.0.1" - "@atlaskit/ds-explorations@^2.2.0": version "2.2.12" resolved "https://registry.yarnpkg.com/@atlaskit/ds-explorations/-/ds-explorations-2.2.12.tgz#6f6060b34b4f9843f2bf01d25e99dce12fc4839e" @@ -231,16 +147,6 @@ "@emotion/react" "^11.7.1" tiny-invariant "^1.2.0" -"@atlaskit/ds-explorations@^3.0.0": - version "3.0.6" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/ds-explorations/-/ds-explorations-3.0.6.tgz#ebe7097bc491bdd080133175efc8b99a3d8b70e0" - integrity sha512-JIZtkGvUlfVZENDTqhKuI/SXOntD4Cy+V12jUqYZQPNtyv8WlfOUErj+1y0lxKQKYI4wOhflKwK2rTtWmJOQsQ== - dependencies: - "@atlaskit/tokens" "^1.29.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - tiny-invariant "^1.2.0" - "@atlaskit/ds-lib@^2.2.0": version "2.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/ds-lib/-/ds-lib-2.2.3.tgz#fc65a829b45ee0a26c9c6c97072e2d570214aec7" @@ -298,15 +204,6 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" -"@atlaskit/heading@^1.4.0": - version "1.4.3" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/heading/-/heading-1.4.3.tgz#dccf63117fe0600c1994010eb4a5a97214f3ca41" - integrity sha512-29VbpehvlUmAel4SqS+KFcGj2/y5M7ZMfW2VWHlOfU0JDEL0GCd+RBwqg23iVsSf2k9N77yE6CJ7HSRmWS1aSQ== - dependencies: - "@atlaskit/tokens" "^1.25.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - "@atlaskit/icon@^21.12.0": version "21.12.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/icon/-/icon-21.12.5.tgz#834c967b0e62ceb6bb4951a36a50fda773a5f9ce" @@ -327,15 +224,6 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" -"@atlaskit/icon@^22.0.0": - version "22.0.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/icon/-/icon-22.0.0.tgz#f267fe623827eb4ddf3b1632756049d04d19a8b7" - integrity sha512-90sfTQ7O6vehzNY8Qm2SCufwmQQ3A5Pw40yHBEHD5XeiJ0BaGY8fmzxx16JcqLxiUfyDlxml3rsKD++XzksOHg== - dependencies: - "@atlaskit/tokens" "^1.28.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - "@atlaskit/in-product-testing@^0.2.0": version "0.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/in-product-testing/-/in-product-testing-0.2.3.tgz#b56c583951b2c6f5eeb1dced8ff8eefa1cd9cb08" @@ -359,14 +247,6 @@ "@babel/runtime" "^7.0.0" bind-event-listener "^2.1.1" -"@atlaskit/locale@^2.6.0": - version "2.6.2" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/locale/-/locale-2.6.2.tgz#14c7ed315306963b5e1e421bbe62f74a30d3f219" - integrity sha512-MFjig0nQMRB/baMF+8LPJOXmWE1DJqYFPO1bfr0t2giQRhDKZxyvyTLKuij+T/PTK70iSyFzn7bLTNwvPXqhMA== - dependencies: - "@atlaskit/select" "^17.0.0" - "@babel/runtime" "^7.0.0" - "@atlaskit/lozenge@^11.4.3": version "11.4.3" resolved "https://registry.yarnpkg.com/@atlaskit/lozenge/-/lozenge-11.4.3.tgz#bf9ea032bbdc94bd9b24167fa88e150b86426f6a" @@ -485,19 +365,6 @@ "@emotion/serialize" "^1.1.0" bind-event-listener "^2.1.1" -"@atlaskit/primitives@^1.13.0": - version "1.13.1" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/primitives/-/primitives-1.13.1.tgz#c6da57952e06b56439ec0a27f55d722314c1c564" - integrity sha512-mjtL6tma8/PdsWG+F6qCgYLSziZcKo4r/VtdMmCNpVgAU+YJda3HczFey+roLNrtzVMZMKg4msm4SO8UqHi05A== - dependencies: - "@atlaskit/app-provider" "^0.4.0" - "@atlaskit/tokens" "^1.29.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - "@emotion/serialize" "^1.1.0" - bind-event-listener "^2.1.1" - tiny-invariant "^1.2.0" - "@atlaskit/primitives@^1.6.0": version "1.6.7" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/primitives/-/primitives-1.6.7.tgz#03217a8497341c6fefc673638a4a58f2a1f1b5c9" @@ -535,31 +402,6 @@ react-uid "^2.2.0" shallow-equal "^1.0.0" -"@atlaskit/select@^17.0.0": - version "17.0.3" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/select/-/select-17.0.3.tgz#03fbee4d6caa74c7ee4660107f3aaa42be9efd42" - integrity sha512-YEr7plvrlqw4z/dGc6srKZI8QsyBkMf63p7PAS7mLIYuAnPG1eONwcRoVLqBVeAJeWcK5is1GUdQM5Z6kdJ5xw== - dependencies: - "@atlaskit/analytics-next" "^9.1.0" - "@atlaskit/icon" "^22.0.0" - "@atlaskit/platform-feature-flags" "^0.2.0" - "@atlaskit/spinner" "^16.0.0" - "@atlaskit/theme" "^12.6.0" - "@atlaskit/tokens" "^1.29.0" - "@atlaskit/visually-hidden" "^1.2.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - "@popperjs/core" "^2.9.1" - bind-event-listener "^2.1.1" - memoize-one "^6.0.0" - react-fast-compare "^3.2.0" - react-focus-lock "^2.9.5" - react-node-resolver "^1.0.1" - react-popper "^2.2.3" - react-select "^5.4.0" - react-uid "^2.2.0" - shallow-equal "^3.1.0" - "@atlaskit/skeleton@^0.2.3": version "0.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/skeleton/-/skeleton-0.2.3.tgz#e56db536f01adaeb0dfc9eb39478337c09315e96" @@ -591,17 +433,6 @@ "@babel/runtime" "^7.0.0" "@emotion/react" "^11.7.1" -"@atlaskit/spinner@^16.0.0": - version "16.0.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/spinner/-/spinner-16.0.0.tgz#e59f2bea3aaafaf5b0e23999d96cb1c5fc777a90" - integrity sha512-KZq+YiwO9lb9VFHBZisVq5/ToyhkhxgLSmPIIm8K+mHoTt7CGk9+Iz04Oui5OYg6Sq5XN1SVklWCuDEhiJcmlQ== - dependencies: - "@atlaskit/interaction-context" "^2.1.0" - "@atlaskit/theme" "^12.6.0" - "@atlaskit/tokens" "^1.28.0" - "@babel/runtime" "^7.0.0" - "@emotion/react" "^11.7.1" - "@atlaskit/textarea@^4.7.7": version "4.7.7" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/textarea/-/textarea-4.7.7.tgz#eafa1096f8cdbe65bff26e9b82ac241236bf7d80" @@ -670,30 +501,6 @@ "@babel/types" "^7.20.0" bind-event-listener "^2.1.1" -"@atlaskit/tokens@^1.28.0": - version "1.29.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/tokens/-/tokens-1.29.0.tgz#812ddab4d1baacec80f57934b92823bb9fcfd6ba" - integrity sha512-VDSl6+HKwInNt9mHXO2M2wKlWDo9rCOFhcAbXD+V3z5AsM7ccjX7Dl9Z2H9lciV6o+pkS0FkdE83DfJVgCBiRg== - dependencies: - "@atlaskit/ds-lib" "^2.2.0" - "@atlaskit/platform-feature-flags" "^0.2.0" - "@babel/runtime" "^7.0.0" - "@babel/traverse" "^7.23.2" - "@babel/types" "^7.20.0" - bind-event-listener "^2.1.1" - -"@atlaskit/tokens@^1.29.0": - version "1.29.2" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/tokens/-/tokens-1.29.2.tgz#0f909d5315c002bcda6349039d11c544c0c23351" - integrity sha512-gxJSfvpDPmVDPt76swB1GFPhrAlKcKnmKtyP/I18amBbvvW3dmcPWJn94f1e78Aax363ozfcmGbsYj/NK3bZ6A== - dependencies: - "@atlaskit/ds-lib" "^2.2.0" - "@atlaskit/platform-feature-flags" "^0.2.0" - "@babel/runtime" "^7.0.0" - "@babel/traverse" "^7.23.2" - "@babel/types" "^7.20.0" - bind-event-listener "^2.1.1" - "@atlaskit/tokens@^1.4.0", "@atlaskit/tokens@^1.8.0": version "1.10.1" resolved "https://packages.atlassian.com/api/npm/npm-remote/@atlaskit/tokens/-/tokens-1.10.1.tgz#28e32d1205bf7f771e0f523dea17fbecac297bdb" @@ -758,14 +565,6 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": - version "7.23.5" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== - dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" - "@babel/compat-data@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" @@ -847,16 +646,6 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/generator@^7.23.5": - version "7.23.5" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/generator/-/generator-7.23.5.tgz#17d0a1ea6b62f351d281350a5f80b87a810c4755" - integrity sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA== - dependencies: - "@babel/types" "^7.23.5" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -928,11 +717,6 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - "@babel/helper-environment-visitor@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" @@ -946,14 +730,6 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -1063,16 +839,6 @@ resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" @@ -1119,25 +885,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== -"@babel/parser@^7.22.15", "@babel/parser@^7.23.5": - version "7.23.5" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/parser/-/parser-7.23.5.tgz#37dee97c4752af148e1d38c34b856b2507660563" - integrity sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ== - "@babel/parser@^7.22.7": version "7.22.7" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" @@ -1998,22 +1750,6 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.21.0": - version "7.23.5" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/runtime/-/runtime-7.23.5.tgz#11edb98f8aeec529b82b211028177679144242db" - integrity sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" - "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -2055,22 +1791,6 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.23.2": - version "7.23.5" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/traverse/-/traverse-7.23.5.tgz#f546bf9aba9ef2b042c0e00d245990c15508e7ec" - integrity sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.5" - "@babel/types" "^7.23.5" - debug "^4.1.0" - globals "^11.1.0" - "@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.22.5" resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" @@ -2080,15 +1800,6 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" -"@babel/types@^7.22.15", "@babel/types@^7.23.0", "@babel/types@^7.23.5": - version "7.23.5" - resolved "https://packages.atlassian.com/api/npm/npm-remote/@babel/types/-/types-7.23.5.tgz#48d730a00c95109fa4393352705954d74fb5b602" - integrity sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w== - dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://packages.atlassian.com/api/npm/npm-remote/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -4705,7 +4416,7 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://packages.atlassian.com/api/npm/npm-remote/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== -chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://packages.atlassian.com/api/npm/npm-remote/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -5262,13 +4973,6 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" -date-fns@^2.17.0: - version "2.30.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" - integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== - dependencies: - "@babel/runtime" "^7.21.0" - debug@2.6.9, debug@^2.6.0: version "2.6.9" resolved "https://packages.atlassian.com/api/npm/npm-remote/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -10570,11 +10274,6 @@ shallow-equal@^1.0.0: resolved "https://packages.atlassian.com/api/npm/npm-remote/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== -shallow-equal@^3.1.0: - version "3.1.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/shallow-equal/-/shallow-equal-3.1.0.tgz#e7a54bac629c7f248eff6c2f5b63122ba4320bec" - integrity sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg== - shebang-command@^2.0.0: version "2.0.0" resolved "https://packages.atlassian.com/api/npm/npm-remote/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" diff --git a/test/snapshots/app.test.ts.snap b/test/snapshots/app.test.ts.snap index 85a64b2323..30e6e964c5 100644 --- a/test/snapshots/app.test.ts.snap +++ b/test/snapshots/app.test.ts.snap @@ -15,10 +15,10 @@ exports[`app getFrontendApp please review routes and update snapshot when adding query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,serveStatic :GET ^/?(?=/|$)^/rest/?(?=/|$)^/subscriptions/?(?=/|$)^/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionsGet -:POST ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/sync/?$ - query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,middleware,JwtHandler,jiraAdminEnforceMiddleware,AnalyticsProxyHandler :DELETE ^/?(?=/|$)^/rest/?(?=/|$)^/subscriptions/?(?=/|$)^/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionDelete +:POST ^/?(?=/|$)^/rest/?(?=/|$)^/subscriptions/?(?=/|$)^/sync/?$ + query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,AnalyticsProxyHandler :GET ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/github-callback/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,OAuthCallbackHandler :GET ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/github-installed/?$ @@ -47,6 +47,8 @@ exports[`app getFrontendApp please review routes and update snapshot when adding query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionsGet :DELETE ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/subscriptions/(?:([^/]+?))/?(?=/|$)^/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,SubscriptionDelete +:POST ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/subscriptions/(?:([^/]+?))/?(?=/|$)^/sync/?$ + query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,AnalyticsProxyHandler :GET ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/org/?(?=/|$)^/?$ query,expressInit,elapsedTimeMetrics,sentryRequestMiddleware,urlencodedParser,jsonParser,cookieParser,LogMiddleware,JwtHandler,jiraAdminEnforceMiddleware,GitHubTokenHandler,GitHubOrgsFetchOrgs :POST ^/?(?=/|$)^/rest/?(?=/|$)^/app/(?:([^/]+?))/?(?=/|$)^/org/?(?=/|$)^/?$ From 1f6533f309827926b70abdb9ce7f43ba6b47b8a8 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 14:41:02 +1100 Subject: [PATCH 20/26] chore: revoke unnecessary changes --- src/rest-interfaces/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rest-interfaces/index.ts b/src/rest-interfaces/index.ts index 9ec5151707..44b9b32c6d 100644 --- a/src/rest-interfaces/index.ts +++ b/src/rest-interfaces/index.ts @@ -183,6 +183,4 @@ export type GhEnterpriseServer = { export type GHSubscriptions = { ghCloudSubscriptions: GhCloudSubscriptions; ghEnterpriseServers: GhEnterpriseServer[]; -}; - -export type BackfillPageModalTypes = "BACKFILL" | "DISCONNECT_SUBSCRIPTION" | "DISCONNECT_SERVER_APP" | "DISCONNECT_SERVER"; +}; \ No newline at end of file From 7888cd013c6308ef05d3fcfe1619fdb2b22f8a2e Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 14:50:43 +1100 Subject: [PATCH 21/26] chore: revoke unnecessary changes --- src/rest-interfaces/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest-interfaces/index.ts b/src/rest-interfaces/index.ts index 44b9b32c6d..10194b09b7 100644 --- a/src/rest-interfaces/index.ts +++ b/src/rest-interfaces/index.ts @@ -183,4 +183,4 @@ export type GhEnterpriseServer = { export type GHSubscriptions = { ghCloudSubscriptions: GhCloudSubscriptions; ghEnterpriseServers: GhEnterpriseServer[]; -}; \ No newline at end of file +}; From f1a77b2dfe46bd3291ebe5cad8e3976185a9edcf Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 14:53:39 +1100 Subject: [PATCH 22/26] chore: revoke unnecessary changes --- spa/src/pages/Connected/index.tsx | 2 +- spa/src/pages/Connected/test.tsx | 2 +- spa/src/pages/Connections/GHCloudConnections/index.tsx | 2 +- spa/src/pages/Connections/index.tsx | 2 +- spa/src/utils/dynamicTableHelper.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spa/src/pages/Connected/index.tsx b/spa/src/pages/Connected/index.tsx index 5145f9cef0..a004977c3f 100644 --- a/spa/src/pages/Connected/index.tsx +++ b/spa/src/pages/Connected/index.tsx @@ -150,4 +150,4 @@ const Connected = () => { ); }; -export default Connected; \ No newline at end of file +export default Connected; diff --git a/spa/src/pages/Connected/test.tsx b/spa/src/pages/Connected/test.tsx index 1192493d07..82e270b4d0 100644 --- a/spa/src/pages/Connected/test.tsx +++ b/spa/src/pages/Connected/test.tsx @@ -53,4 +53,4 @@ test("Basic check for the Connected Page", async () => { await act(() => userEvent.click(screen.getByText("Add another organization"))); expect(navigate).toHaveBeenCalledWith("/spa/steps"); -}); \ No newline at end of file +}); diff --git a/spa/src/pages/Connections/GHCloudConnections/index.tsx b/spa/src/pages/Connections/GHCloudConnections/index.tsx index 6f60cafd28..9492ca30d9 100644 --- a/spa/src/pages/Connections/GHCloudConnections/index.tsx +++ b/spa/src/pages/Connections/GHCloudConnections/index.tsx @@ -30,4 +30,4 @@ const GitHubCloudConnections = ({ ); }; -export default GitHubCloudConnections; \ No newline at end of file +export default GitHubCloudConnections; diff --git a/spa/src/pages/Connections/index.tsx b/spa/src/pages/Connections/index.tsx index 35832397ae..905091c9b3 100644 --- a/spa/src/pages/Connections/index.tsx +++ b/spa/src/pages/Connections/index.tsx @@ -58,4 +58,4 @@ const Connections = () => { ); }; -export default Connections; \ No newline at end of file +export default Connections; diff --git a/spa/src/utils/dynamicTableHelper.tsx b/spa/src/utils/dynamicTableHelper.tsx index 14a30b67e8..1a62a23094 100644 --- a/spa/src/utils/dynamicTableHelper.tsx +++ b/spa/src/utils/dynamicTableHelper.tsx @@ -151,4 +151,4 @@ export const getGHSubscriptionsRows = ( ], }) ); -}; \ No newline at end of file +}; From f478afcf5ecd00a04754b7dee62e936ff34e48f4 Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 14:57:40 +1100 Subject: [PATCH 23/26] chore: revoke unnecessary changes --- src/rest/routes/subscriptions/sync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest/routes/subscriptions/sync.ts b/src/rest/routes/subscriptions/sync.ts index 372b7494fb..0bf38d56b8 100644 --- a/src/rest/routes/subscriptions/sync.ts +++ b/src/rest/routes/subscriptions/sync.ts @@ -11,7 +11,7 @@ import { RestSyncReqBody } from "~/src/rest-interfaces"; const restSyncPost = async ( req: Request, - res: Response + res: Response ) => { const { syncType: syncTypeFromReq, From c40b726ed6181c94525770ebb85ac7f98867963f Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 15:20:13 +1100 Subject: [PATCH 24/26] chore: revoke unnecessary changes --- src/rest/routes/subscriptions/sync.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rest/routes/subscriptions/sync.ts b/src/rest/routes/subscriptions/sync.ts index 0bf38d56b8..a65cccdaba 100644 --- a/src/rest/routes/subscriptions/sync.ts +++ b/src/rest/routes/subscriptions/sync.ts @@ -5,7 +5,7 @@ import { Subscription } from "models/subscription"; import { findOrStartSync } from "~/src/sync/sync-utils"; import { determineSyncTypeAndTargetTasks } from "~/src/util/github-sync-helper"; import { BaseLocals } from ".."; -import { RestApiError } from "~/src/config/errors"; +import { InsufficientPermissionError, RestApiError } from "~/src/config/errors"; import { RestSyncReqBody } from "~/src/rest-interfaces"; // import { GitHubServerApp } from "~/src/models/github-server-app"; @@ -55,6 +55,7 @@ const restSyncPost = async ( // } const subscription = await Subscription.findByPk(subscriptionId); + if (!subscription) { req.log.info( { @@ -70,6 +71,13 @@ const restSyncPost = async ( ); } + const localJiraHost = res.locals.installation.jiraHost; + + if (subscription.jiraHost !== localJiraHost) { + throw new InsufficientPermissionError("Forbidden - mismatched Jira Host"); + } + + const { syncType, targetTasks } = determineSyncTypeAndTargetTasks( syncTypeFromReq, subscription From 93183abac3b61b299445cc0b6fa6017823831bda Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 15:32:07 +1100 Subject: [PATCH 25/26] chore: revoke unnecessary changes --- src/rest/routes/subscriptions/sync.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/rest/routes/subscriptions/sync.test.ts b/src/rest/routes/subscriptions/sync.test.ts index 68269bf076..1aa22b812f 100644 --- a/src/rest/routes/subscriptions/sync.test.ts +++ b/src/rest/routes/subscriptions/sync.test.ts @@ -8,6 +8,7 @@ import { encodeSymmetric } from "atlassian-jwt"; import { GitHubServerApp } from "models/github-server-app"; import { v4 as newUUID } from "uuid"; import { sqsQueues } from "~/src/sqs/queues"; +import { DatabaseStateCreator } from "~/test/utils/database-state-creator"; jest.mock("~/src/sqs/queues"); jest.mock("config/feature-flags"); @@ -82,6 +83,22 @@ describe("Checking the deferred request parsing route", () => { expect(resp.status).toEqual(401); }); + it("should return 403 on correct sub id with different jiraHost", async () => { + const commitsFromDate = new Date(new Date().getTime() - 2000); + const result = await new DatabaseStateCreator() + .forJiraHost("https://another-one.atlassian.net") + .create(); + return supertest(app) + .post(`/rest/app/cloud/subscriptions/${result.subscription.id}/sync`) + .set("authorization", `${getToken()}`) + .send({ + jiraHost, + syncType: "full", + commitsFromDate + }) + .expect(403); + }); + it("should return 400 on incorrect commitsFromDate", async () => { const commitsFromDate = new Date(new Date().getTime() - 2000); return supertest(app) From 2519132c923f70b4c28896886f5b21fcf470283d Mon Sep 17 00:00:00 2001 From: Kamakshee Samant Date: Thu, 7 Dec 2023 15:57:32 +1100 Subject: [PATCH 26/26] chore: revoke unnecessary changes --- src/rest/routes/subscriptions/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rest/routes/subscriptions/index.ts b/src/rest/routes/subscriptions/index.ts index 2f852879f4..da165809cb 100644 --- a/src/rest/routes/subscriptions/index.ts +++ b/src/rest/routes/subscriptions/index.ts @@ -35,7 +35,6 @@ SubscriptionsRouter.delete("/", errorWrapper("SubscriptionDelete", async (req: R const gitHubAppId = cloudOrUUID === "cloud" ? undefined : (await GitHubServerApp.getForUuidAndInstallationId(cloudOrUUID, installation.id))?.appId; //TODO: validate the uuid regex - // eslint-disable-next-line @typescript-eslint/no-unsafe-call await removeSubscription(installation, undefined, gitHubAppId, req.log, subscriptionId); res.sendStatus(204);