From 72ebb9b40ce10c3eb5ea99bb4ba1956feaa233f1 Mon Sep 17 00:00:00 2001 From: Abe Garcia <44239562+abe21412@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:40:57 -0400 Subject: [PATCH] Cn 2357 deployment tracker add ability to pause and retrigger executions (#10) * add button to pause/resume * verify still works after date-picker merge * add retrigger button * add proxy config for broadside * added styling for retrigger/pause buttons --------- Co-authored-by: abe garcia Co-authored-by: Victor --- .husky/pre-commit | 8 +- spin-observatory-plugin-deck/package.json | 7 +- .../src/components/PluginContainer.tsx | 9 +- .../actions/ActionButtonsContainer.tsx | 15 ++ .../components/actions/PauseResumeButton.tsx | 118 ++++++++++ .../components/actions/RetriggerButton.tsx | 63 ++++++ .../src/components/actions/index.ts | 3 + .../components/date-picker/date-picker.tsx | 87 +++----- .../src/components/pipelines/ExecutionRow.tsx | 11 +- .../components/pipelines/ExecutionsTable.tsx | 47 ++-- .../pipelines/PipelineExecutions.tsx | 57 +++-- .../src/components/status/StatusSelect.tsx | 34 +-- .../src/services/broadside.ts | 29 +++ .../src/services/gate.ts | 42 ++++ .../src/services/gateService.ts | 22 -- .../src/services/index.ts | 2 + spin-observatory-plugin-deck/yarn.lock | 209 ++++-------------- 17 files changed, 460 insertions(+), 303 deletions(-) create mode 100644 spin-observatory-plugin-deck/src/components/actions/ActionButtonsContainer.tsx create mode 100644 spin-observatory-plugin-deck/src/components/actions/PauseResumeButton.tsx create mode 100644 spin-observatory-plugin-deck/src/components/actions/RetriggerButton.tsx create mode 100644 spin-observatory-plugin-deck/src/components/actions/index.ts create mode 100644 spin-observatory-plugin-deck/src/services/broadside.ts create mode 100644 spin-observatory-plugin-deck/src/services/gate.ts delete mode 100644 spin-observatory-plugin-deck/src/services/gateService.ts create mode 100644 spin-observatory-plugin-deck/src/services/index.ts diff --git a/.husky/pre-commit b/.husky/pre-commit index 8329baa..9e9abcc 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,6 @@ -# #!/usr/bin/env bash -# # CHECK THIS FILE INTO SOURCE CONTROL! +#!/usr/bin/env bash +# CHECK THIS FILE INTO SOURCE CONTROL! -# . "$(dirname "$0")/_/husky.sh" +. "$(dirname "$0")/_/husky.sh" -# [[ -e spin-observatory-plugin-deck/node_modules/.bin/lint-staged ]] && cd spin-observatory-plugin-deck; ./node_modules/.bin/lint-staged -p false +[[ -e spin-observatory-plugin-deck/node_modules/.bin/lint-staged ]] && cd spin-observatory-plugin-deck; ./node_modules/.bin/lint-staged -p false diff --git a/spin-observatory-plugin-deck/package.json b/spin-observatory-plugin-deck/package.json index 083ba2a..0fe547b 100644 --- a/spin-observatory-plugin-deck/package.json +++ b/spin-observatory-plugin-deck/package.json @@ -19,19 +19,17 @@ "host": "https://dev.spinnaker.homedepot.com/" }, "dependencies": { - "@emotion/react": "^11.10.6", - "@emotion/styled": "^11.10.6", "@date-io/date-fns": "1", "@material-ui/core": "^4.12.4", - "@material-ui/pickers": "^3.3.10", "@material-ui/icons": "^4.11.3", "@material-ui/lab": "^4.0.0-alpha.61", + "@material-ui/pickers": "^3.3.10", "@rollup/plugin-commonjs": "18.0.0", "@rollup/plugin-typescript": "8.2.1", "@rollup/plugin-url": "6.0.0", "@spinnaker/core": "0.23.0", "@spinnaker/pluginsdk": "0.3.0", - "@spinnaker/pluginsdk-peerdeps": "0.11.0", + "@spinnaker/pluginsdk-peerdeps": "0.15.0", "@spinnaker/presentation": "0.2.1", "@types/react-dom": "18.0.11", "@uirouter/core": "6.0.4", @@ -46,6 +44,7 @@ "react-virtualized-select": "^3.1.3", "rollup": "2.45.2", "rxjs": "6.6.7", + "sass": "^1.62.1", "typescript": "4.9.5" }, "devDependencies": { diff --git a/spin-observatory-plugin-deck/src/components/PluginContainer.tsx b/spin-observatory-plugin-deck/src/components/PluginContainer.tsx index a405abc..3358ae9 100644 --- a/spin-observatory-plugin-deck/src/components/PluginContainer.tsx +++ b/spin-observatory-plugin-deck/src/components/PluginContainer.tsx @@ -4,10 +4,11 @@ import React, { useEffect, useState } from 'react'; import type { Application, IPipeline } from '@spinnaker/core'; import { ReactSelectInput, useDataSource } from '@spinnaker/core'; -import { DatePicker, IDateRange } from './date-picker/date-picker'; +import type { IDateRange } from './date-picker/date-picker'; +import { DatePicker } from './date-picker/date-picker'; import { ParameterSelect } from './parameters'; +import { MAX_DATE_RANGE, PipelineExecutions } from './pipelines'; import { StatusSelect } from './status'; -import { PipelineExecutions, MAX_DATE_RANGE } from './pipelines'; interface IPluginContainerProps { app: Application; @@ -34,7 +35,7 @@ export function PluginContainer({ app }: IPluginContainerProps) { setSelectedPipeline(pipelineConfig); }; - const handleDateFilterChange = ({ start, end }: { start: number, end: number }) => { + const handleDateFilterChange = ({ start, end }: { start: number; end: number }) => { setSelectedDateRange({ start, end }); }; @@ -69,7 +70,7 @@ export function PluginContainer({ app }: IPluginContainerProps) { diff --git a/spin-observatory-plugin-deck/src/components/actions/ActionButtonsContainer.tsx b/spin-observatory-plugin-deck/src/components/actions/ActionButtonsContainer.tsx new file mode 100644 index 0000000..673579b --- /dev/null +++ b/spin-observatory-plugin-deck/src/components/actions/ActionButtonsContainer.tsx @@ -0,0 +1,15 @@ +import Grid from '@material-ui/core/Grid'; +import type { ReactNode } from 'react'; +import React, { Children } from 'react'; + +interface IActionButtonsContainerProps { + children: NonNullable; +} + +export const ActionButtonsContainer = ({ children }: IActionButtonsContainerProps) => ( + + {Children.toArray(children).map((child) => ( + {child} + ))} + +); diff --git a/spin-observatory-plugin-deck/src/components/actions/PauseResumeButton.tsx b/spin-observatory-plugin-deck/src/components/actions/PauseResumeButton.tsx new file mode 100644 index 0000000..ea9f8cf --- /dev/null +++ b/spin-observatory-plugin-deck/src/components/actions/PauseResumeButton.tsx @@ -0,0 +1,118 @@ +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import Grow from '@material-ui/core/Grow'; +import MenuItem from '@material-ui/core/MenuItem'; +import MenuList from '@material-ui/core/MenuList'; +import Paper from '@material-ui/core/Paper'; +import Popper from '@material-ui/core/Popper'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; +import React, { Fragment, useRef, useState } from 'react'; +import { gate } from '../../services/'; + +interface IPauseResumeButtonProps { + executionIds: string[]; + refreshExecutions: () => void; +} + +const options = [ + { text: 'Pause', action: gate.pauseExecutions }, + { text: 'Resume', action: gate.resumeExecutions }, +]; + +export const PauseResumeButton = ({ executionIds, refreshExecutions }: IPauseResumeButtonProps) => { + const [open, setOpen] = useState(false); + const anchorRef = useRef(null); + const [selectedIndex, setSelectedIndex] = useState(0); + const [hover, setHover] = useState(false); + + const handleButtonClick = () => { + options[selectedIndex].action(executionIds).then(() => refreshExecutions()); + }; + + const handleMenuItemClick = (idx: number) => (_: React.MouseEvent) => { + setSelectedIndex(idx); + setOpen(false); + }; + + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + const handleClose = (event: React.MouseEvent) => { + if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) { + return; + } + + setOpen(false); + }; + + const handleHover = () => setHover((prevHover) => !prevHover); + + const disabled = executionIds.length === 0; + + const computeBtnColor = () => { + if (disabled) { + return 'var(--color-status-inactive)'; + } else if (hover) { + return 'var(--button-primary-hover-bg)'; + } else { + return 'var(--color-accent)'; + } + }; + + return ( + + + + + + + {({ TransitionProps }) => ( + + + + + {options.map((option, idx) => ( + + {option.text} + + ))} + + + + + )} + + + ); +}; diff --git a/spin-observatory-plugin-deck/src/components/actions/RetriggerButton.tsx b/spin-observatory-plugin-deck/src/components/actions/RetriggerButton.tsx new file mode 100644 index 0000000..6915012 --- /dev/null +++ b/spin-observatory-plugin-deck/src/components/actions/RetriggerButton.tsx @@ -0,0 +1,63 @@ +import { Button, ButtonGroup } from '@material-ui/core'; +import React, { useState } from 'react'; + +import type { IExecution } from '@spinnaker/core'; + +import { broadside } from '../../services/'; + +interface IRetriggerButtonProps { + executions: IExecution[]; + refreshExecutions: () => void; +} + +export const RetriggerButton = ({ executions, refreshExecutions }: IRetriggerButtonProps) => { + const [retriggerInProgress, setRetriggerInProgress] = useState(false); + const [hover, setHover] = useState(false); + + const handleHover = () => setHover((prevHover) => !prevHover); + + const disabled = executions.length === 0 || retriggerInProgress; + + const handleRetrigger = () => { + setRetriggerInProgress(true); + broadside + .retriggerExecutions({ executions }) + .then((res) => { + /* eslint-disable no-console */ + console.log(res.status); + return res.json(); + }) + /* eslint-disable no-console */ + .then((data) => console.log(data)) + .catch((e) => console.error('error retriggering: ', e)) + .finally(() => { + setRetriggerInProgress(false); + refreshExecutions(); + }); + }; + + const computeBtnColor = () => { + if (disabled) { + return 'var(--color-status-inactive)'; + } else if (hover) { + return 'var(--button-primary-hover-bg)'; + } else { + return 'var(--color-accent)'; + } + }; + + return ( + + + + ); +}; diff --git a/spin-observatory-plugin-deck/src/components/actions/index.ts b/spin-observatory-plugin-deck/src/components/actions/index.ts new file mode 100644 index 0000000..748b860 --- /dev/null +++ b/spin-observatory-plugin-deck/src/components/actions/index.ts @@ -0,0 +1,3 @@ +export * from './PauseResumeButton'; +export * from './RetriggerButton'; +export * from './ActionButtonsContainer'; diff --git a/spin-observatory-plugin-deck/src/components/date-picker/date-picker.tsx b/spin-observatory-plugin-deck/src/components/date-picker/date-picker.tsx index 876fdfc..59ef00e 100644 --- a/spin-observatory-plugin-deck/src/components/date-picker/date-picker.tsx +++ b/spin-observatory-plugin-deck/src/components/date-picker/date-picker.tsx @@ -1,18 +1,8 @@ -import React, { useEffect, useState } from "react"; -import { - Button, - TextField, - MenuItem, - Menu, - ListSubheader, - PopoverProps -} from "@material-ui/core"; - -import { - MuiPickersUtilsProvider, - KeyboardDateTimePicker -} from "@material-ui/pickers"; -import DateFnsUtils from "@date-io/date-fns"; +import DateFnsUtils from '@date-io/date-fns'; +import type { PopoverProps } from '@material-ui/core'; +import { Button, ListSubheader, Menu, MenuItem, TextField } from '@material-ui/core'; +import { KeyboardDateTimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'; +import React, { useEffect, useState } from 'react'; export interface IDateRange { start: number; @@ -27,35 +17,35 @@ const START_AND_END_BY_HOURS = ({ hours = 1 }) => { const PREMADE_SELECTIONS = [ { - value: "hour", - text: "Last Hour", - calculation: () => START_AND_END_BY_HOURS({ hours: 1 }) + value: 'hour', + text: 'Last Hour', + calculation: () => START_AND_END_BY_HOURS({ hours: 1 }), }, { - value: "day", - text: "Last 24 Hours", - calculation: () => START_AND_END_BY_HOURS({ hours: 24 }) + value: 'day', + text: 'Last 24 Hours', + calculation: () => START_AND_END_BY_HOURS({ hours: 24 }), }, { - value: "week", - text: "Last 7 Days", - calculation: () => START_AND_END_BY_HOURS({ hours: 24 * 7 }) + value: 'week', + text: 'Last 7 Days', + calculation: () => START_AND_END_BY_HOURS({ hours: 24 * 7 }), }, { - value: "month", - text: "Last 30 Days", - calculation: () => START_AND_END_BY_HOURS({ hours: 24 * 30 }) - } + value: 'month', + text: 'Last 30 Days', + calculation: () => START_AND_END_BY_HOURS({ hours: 24 * 30 }), + }, ]; type DatePickerProps = { - disabled?: boolean, - onChange: ({ start, end }: { start?: number, end?: number }) => void + disabled?: boolean; + onChange: ({ start, end }: { start?: number; end?: number }) => void; }; -export const DatePicker = ({ disabled, onChange }:DatePickerProps) => { +export const DatePicker = ({ disabled, onChange }: DatePickerProps) => { const [anchorEl, setAnchorEl] = useState(null); - const [value, setValue] = useState(""); + const [value, setValue] = useState(''); const [selectedCustomStart, setSelectedCustomStart] = useState(new Date()); const [selectedCustomEnd, setSelectedCustomEnd] = useState(new Date()); @@ -77,7 +67,7 @@ export const DatePicker = ({ disabled, onChange }:DatePickerProps) => { }; const handleMenuItemClick = (newValue: string) => { - const item = PREMADE_SELECTIONS.find(i => i.value === newValue); + const item = PREMADE_SELECTIONS.find((i) => i.value === newValue); setValue(item.text); onChange(item.calculation()); handleClose(); @@ -92,9 +82,7 @@ export const DatePicker = ({ disabled, onChange }:DatePickerProps) => { }; const updateValueWithCustomDateRange = () => { - setValue( - `${selectedCustomStart.toLocaleDateString()} - ${selectedCustomEnd.toLocaleDateString()}` - ); + setValue(`${selectedCustomStart.toLocaleDateString()} - ${selectedCustomEnd.toLocaleDateString()}`); handleClose(); }; @@ -105,35 +93,24 @@ export const DatePicker = ({ disabled, onChange }:DatePickerProps) => { disabled={disabled} select fullWidth - style={disabled ? {} : { backgroundColor: "var(--color-white)" }} + style={disabled ? {} : { backgroundColor: 'var(--color-white)' }} size="small" label="Filter Date Range" variant="outlined" value={value} onClick={handleClick} InputProps={{ - readOnly: true + readOnly: true, }} > {PREMADE_SELECTIONS.map((option) => { return {option.text}; })} - {PREMADE_SELECTIONS.findIndex((p) => p.value === value) === -1 && ( - {value} - )} + {PREMADE_SELECTIONS.findIndex((p) => p.value === value) === -1 && {value}} - + {PREMADE_SELECTIONS.map((option) => { - return ( - handleMenuItemClick(option.value)}> - {option.text} - - ); + return handleMenuItemClick(option.value)}>{option.text}; })} Custom @@ -143,7 +120,7 @@ export const DatePicker = ({ disabled, onChange }:DatePickerProps) => { value={selectedCustomStart} onChange={handleStartDateChange} KeyboardButtonProps={{ - "aria-label": "change start date" + 'aria-label': 'change start date', }} /> { value={selectedCustomEnd} onChange={handleEndDateChange} KeyboardButtonProps={{ - "aria-label": "change end date" + 'aria-label': 'change end date', }} /> @@ -161,4 +138,4 @@ export const DatePicker = ({ disabled, onChange }:DatePickerProps) => { ); -} +}; diff --git a/spin-observatory-plugin-deck/src/components/pipelines/ExecutionRow.tsx b/spin-observatory-plugin-deck/src/components/pipelines/ExecutionRow.tsx index f87559c..33b9d26 100644 --- a/spin-observatory-plugin-deck/src/components/pipelines/ExecutionRow.tsx +++ b/spin-observatory-plugin-deck/src/components/pipelines/ExecutionRow.tsx @@ -1,7 +1,10 @@ -import { TableCell, TableRow, Typography, Checkbox } from '@material-ui/core'; -import React, { MouseEventHandler } from 'react'; -import { IExecution, ReactInjector } from '@spinnaker/core'; +import { Checkbox, TableCell, TableRow, Typography } from '@material-ui/core'; import { makeStyles } from '@material-ui/core'; +import type { MouseEventHandler } from 'react'; +import React from 'react'; + +import type { IExecution } from '@spinnaker/core'; +import { ReactInjector } from '@spinnaker/core'; const useStyles = makeStyles({ tableRow: { @@ -27,7 +30,7 @@ const goToExecutionDetails = (executionId: string) => () => { const convertTimestamp = (ts: number) => { if (!ts) { - return '' + return ''; } return new Intl.DateTimeFormat('en-US', { diff --git a/spin-observatory-plugin-deck/src/components/pipelines/ExecutionsTable.tsx b/spin-observatory-plugin-deck/src/components/pipelines/ExecutionsTable.tsx index 25c3480..1c5f7c5 100644 --- a/spin-observatory-plugin-deck/src/components/pipelines/ExecutionsTable.tsx +++ b/spin-observatory-plugin-deck/src/components/pipelines/ExecutionsTable.tsx @@ -2,19 +2,24 @@ import { Paper, Table, TableBody, + TableCell, TableContainer, TableFooter, - TableRow, TablePagination, + TableRow, Typography, } from '@material-ui/core'; -import React, { ChangeEvent, useState } from 'react'; -import { IExecution } from '@spinnaker/core'; -import { TableHeaders } from './TableHeaders'; +import { makeStyles } from '@material-ui/core'; +import type { ChangeEvent } from 'react'; +import React, { useState } from 'react'; + +import type { IExecution } from '@spinnaker/core'; + import { ExecutionRow } from './ExecutionRow'; -import { DEFAULT_ROWS_PER_PAGE } from './constants'; import { PaginationActions } from './PaginationActions'; -import { makeStyles } from '@material-ui/core'; +import { TableHeaders } from './TableHeaders'; +import { ActionButtonsContainer, PauseResumeButton, RetriggerButton } from '../actions'; +import { DEFAULT_ROWS_PER_PAGE } from './constants'; const useStyles = makeStyles({ tableContainer: { borderRadius: 'inherit' }, @@ -24,10 +29,11 @@ const useStyles = makeStyles({ interface IExecutionsTableProps { executions: IExecution[]; parameters: string[]; + refreshExecutions: () => void; } -export const ExecutionsTable = ({ executions, parameters }: IExecutionsTableProps) => { - const [selectedExecutions, setSelectedExecutions] = useState([]); +export const ExecutionsTable = ({ executions, parameters, refreshExecutions }: IExecutionsTableProps) => { + const [selectedExecutionIds, setSelectedExecutionIds] = useState([]); const [currentPage, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(DEFAULT_ROWS_PER_PAGE); const styles = useStyles(); @@ -45,26 +51,26 @@ export const ExecutionsTable = ({ executions, parameters }: IExecutionsTableProp const handleSelectAll = (e: ChangeEvent) => { if (e.target.checked) { - setSelectedExecutions(executions.map((e) => e.id)); + setSelectedExecutionIds(executions.map((e) => e.id)); return; } - setSelectedExecutions([]); + setSelectedExecutionIds([]); }; const handleSelectOne = (executionId: string) => () => { - const selectedIdx = selectedExecutions.indexOf(executionId); + const selectedIdx = selectedExecutionIds.indexOf(executionId); let newSelected: string[] = []; if (selectedIdx === -1) { - newSelected = [...selectedExecutions, executionId]; + newSelected = [...selectedExecutionIds, executionId]; } else { - newSelected = selectedExecutions.filter((e) => e !== executionId); + newSelected = selectedExecutionIds.filter((e) => e !== executionId); } - setSelectedExecutions(newSelected); + setSelectedExecutionIds(newSelected); }; - const isSelected = (name: string) => selectedExecutions.indexOf(name) !== -1; + const isSelected = (name: string) => selectedExecutionIds.indexOf(name) !== -1; return ( @@ -73,7 +79,7 @@ export const ExecutionsTable = ({ executions, parameters }: IExecutionsTableProp headers={headers} onSelectAll={handleSelectAll} rowCount={executions.length} - selectedCount={selectedExecutions.length} + selectedCount={selectedExecutionIds.length} /> {executions.slice(currentPage * rowsPerPage, currentPage * rowsPerPage + rowsPerPage).map((e) => ( @@ -88,6 +94,15 @@ export const ExecutionsTable = ({ executions, parameters }: IExecutionsTableProp + + + + selectedExecutionIds.includes(e.id))} + refreshExecutions={refreshExecutions} + /> + + ) => void; } -export const PipelineExecutions = ({ appName, pipeline, parameters, statuses, dateRange, onStatusChange }: IPipelineExecutionsProps) => { +export const PipelineExecutions = ({ + appName, + pipeline, + parameters, + statuses, + dateRange, + onStatusChange, +}: IPipelineExecutionsProps) => { const [executions, setExecutions] = useState([]); const [filteredExecutions, setFilteredExecutions] = useState([]); const [statusCount, setStatusCount] = useState>(new Map()); const [isLoading, setIsLoading] = useState(true); const styles = useStyles(); + const getExecutionsParams = { + pipelineName: pipeline.name, + pageSize: REQUEST_PAGE_SIZE, + startDate: dateRange.start, + endDate: dateRange.end, + }; + + const refreshExecutions = () => { + gate.getExecutions(appName, getExecutionsParams).then((resp) => setExecutions(resp)); + }; + useEffect(() => { - if (!pipeline) { + if (!pipeline.name) { setExecutions([]); setFilteredExecutions([]); setStatusCount(new Map()); @@ -46,7 +65,7 @@ export const PipelineExecutions = ({ appName, pipeline, parameters, statuses, da endDate: dateRange.end, }; - getExecutions(appName, requestParams).then((resp) => { + gate.getExecutions(appName, requestParams).then((resp) => { setExecutions(resp); setFilteredExecutions(filterExecutions(resp)); setStatusCount(getStatusCount(resp)); @@ -64,7 +83,7 @@ export const PipelineExecutions = ({ appName, pipeline, parameters, statuses, da useInterval(async () => { if (!pipeline) return; - const resp = await getExecutions(appName, { + const resp = await gate.getExecutions(appName, { pipelineName: pipeline.name, pageSize: REQUEST_PAGE_SIZE, startDate: dateRange.start, @@ -80,11 +99,11 @@ export const PipelineExecutions = ({ appName, pipeline, parameters, statuses, da const filterExecutions = (ex: IExecution[]) => { const statusArr = statuses.length === 0 ? STATUSES : statuses; - return ex.filter(e => statusArr.includes(e.status)); + return ex.filter((e) => statusArr.includes(e.status)); }; const getStatusCount = (ex: IExecution[]) => { - let statusCount = new Map(); + const statusCount = new Map(); for (const e of ex) { if (!statusCount.has(e.status)) { statusCount.set(e.status, 1); @@ -98,15 +117,19 @@ export const PipelineExecutions = ({ appName, pipeline, parameters, statuses, da if (isLoading) { return ( - [...Array(3).keys()].map((key) => ( - - )) +
+ {[...Array(3).keys()].map((key) => ( + + ))} +
); } if (executions.length == 0) { - return

No pipeline executions found.

+ return

No pipeline executions found.

; } - return + return ( + + ); }; diff --git a/spin-observatory-plugin-deck/src/components/status/StatusSelect.tsx b/spin-observatory-plugin-deck/src/components/status/StatusSelect.tsx index 8832613..2df8c66 100644 --- a/spin-observatory-plugin-deck/src/components/status/StatusSelect.tsx +++ b/spin-observatory-plugin-deck/src/components/status/StatusSelect.tsx @@ -5,18 +5,18 @@ import Select from 'react-select'; import type { IPipeline } from '@spinnaker/core'; export const STATUSES = [ - 'SUCCEEDED', - 'FAILED_CONTINUE', - 'TERMINAL', - 'CANCELED', - 'NOT_STARTED', - 'RUNNING', - 'PAUSED', - 'SUSPENDED', - 'BUFFERED', - 'STOPPED', - 'SKIPPED', - 'REDIRECT' + 'SUCCEEDED', + 'FAILED_CONTINUE', + 'TERMINAL', + 'CANCELED', + 'NOT_STARTED', + 'RUNNING', + 'PAUSED', + 'SUSPENDED', + 'BUFFERED', + 'STOPPED', + 'SKIPPED', + 'REDIRECT', ]; interface IStatusSelectProps { @@ -27,13 +27,19 @@ interface IStatusSelectProps { statusCount: Map; } -export const StatusSelect = ({className, pipeline, selectedStatus, setSelectedStatus, statusCount }: IStatusSelectProps) => { +export const StatusSelect = ({ + className, + pipeline, + selectedStatus, + setSelectedStatus, + statusCount, +}: IStatusSelectProps) => { const onStatusSelect = (options: Array>) => { setSelectedStatus(options.map((o) => o.value)); }; const extractStatus = (statusCount: Map): Array> => { - let options = []; + const options: Array> = []; statusCount.forEach((value, key) => { options.push({ label: `${key} (${value})`, value: key }); }); diff --git a/spin-observatory-plugin-deck/src/services/broadside.ts b/spin-observatory-plugin-deck/src/services/broadside.ts new file mode 100644 index 0000000..3dc332f --- /dev/null +++ b/spin-observatory-plugin-deck/src/services/broadside.ts @@ -0,0 +1,29 @@ +import type { IExecution } from '@spinnaker/core'; +import { SETTINGS } from '@spinnaker/core'; + +const BROADSIDE_URI = `${SETTINGS.gateUrl}/proxies/broadside/v1/broadsides`; + +// See https://github.com/one-thd/broadside/blob/main/api/swagger.yml#L206 +const retriggerExecutions = ({ executions }: { executions: IExecution[] }) => { + /*** + * application: "clipper" + * pipelineNameOrId: "Generate Clipper X.509 Key Pair" + * amount: 50 + * delay: 100 + * pipelineBaseParameters: '{ "parameterName": "custom data" }' + * pipelineMultiParameters: '[ { "multiParameterName": "custom data" } ]' + */ + + const application = executions[0].application; + const pipelineNameOrId = executions[0].name; + const amount = executions.length; + const pipelineMultiParameters = executions.map((e) => e.trigger.parameters); + + return fetch(BROADSIDE_URI, { + method: 'POST', + credentials: 'include', + body: JSON.stringify({ application, pipelineNameOrId, amount, pipelineMultiParameters }), + }); +}; + +export const broadside = { retriggerExecutions }; diff --git a/spin-observatory-plugin-deck/src/services/gate.ts b/spin-observatory-plugin-deck/src/services/gate.ts new file mode 100644 index 0000000..352a5fb --- /dev/null +++ b/spin-observatory-plugin-deck/src/services/gate.ts @@ -0,0 +1,42 @@ +import type { IExecution } from '@spinnaker/core'; +import { REST } from '@spinnaker/core'; + +interface IExecutionsParams { + pipelineName: string; + pageSize: number; + startDate: number; + endDate: number; + firstItemIdx?: number; +} + +const getExecutions = async (appName: string, params: IExecutionsParams) => { + const { pipelineName, pageSize, startDate, endDate, firstItemIdx = 0 } = params; + + const data = await REST('/applications') + .path(appName) + .path('executions') + .path('search') + .query({ + pipelineName, + size: pageSize, + startIndex: firstItemIdx, + triggerTimeStartBoundary: startDate, + triggerTimeEndBoundary: endDate, + }) + .get(); + return data; +}; + +const pauseExecution = (executionId: string) => REST('/pipelines').path(executionId).path('pause').put(); + +const pauseExecutions = async (executionIds: string[]) => { + return await Promise.all(executionIds.map((id) => pauseExecution(id))); +}; + +const resumeExecution = (executionId: string) => REST('/pipelines').path(executionId).path('resume').put(); + +const resumeExecutions = async (executionIds: string[]) => { + return await Promise.all(executionIds.map((id) => resumeExecution(id))); +}; + +export const gate = { getExecutions, pauseExecutions, resumeExecutions }; diff --git a/spin-observatory-plugin-deck/src/services/gateService.ts b/spin-observatory-plugin-deck/src/services/gateService.ts deleted file mode 100644 index 3ea47cc..0000000 --- a/spin-observatory-plugin-deck/src/services/gateService.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { IExecution } from '@spinnaker/core'; -import { REST } from '@spinnaker/core'; - -interface IExecutionsParams { - pipelineName: string; - pageSize: number; - startDate: number; - endDate: number; - firstItemIdx?: number; -} - -export const getExecutions = async (appName: string, params: IExecutionsParams) => { - const { pipelineName, pageSize, startDate, endDate, firstItemIdx = 0 } = params; - - const data = await REST('/applications') - .path(appName) - .path('executions') - .path('search') - .query({ pipelineName, size: pageSize, startIndex: firstItemIdx, triggerTimeStartBoundary: startDate, triggerTimeEndBoundary: endDate }) - .get(); - return data; -}; diff --git a/spin-observatory-plugin-deck/src/services/index.ts b/spin-observatory-plugin-deck/src/services/index.ts new file mode 100644 index 0000000..c79be2c --- /dev/null +++ b/spin-observatory-plugin-deck/src/services/index.ts @@ -0,0 +1,2 @@ +export * from './broadside'; +export * from './gate'; diff --git a/spin-observatory-plugin-deck/yarn.lock b/spin-observatory-plugin-deck/yarn.lock index fc9e8cb..ee8a25d 100644 --- a/spin-observatory-plugin-deck/yarn.lock +++ b/spin-observatory-plugin-deck/yarn.lock @@ -11,9 +11,9 @@ "@jridgewell/trace-mapping" "^0.3.9" "@apollo/client@^3.6.9": - version "3.7.13" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.13.tgz#2cd3508d8030f7cb0c3d357dc3f57b5cf8390c66" - integrity sha512-wi63WnO2mhb6uHGB/8x1qIOL4ZtZocrxdHS0VBQ9KwBDkwoP/TdVVgZ29J2WkiAPmJ0SK07ju4R2AjHor1gPxQ== + version "3.7.14" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.14.tgz#40ef90390e6690e94917457cd82bdeb29e8b6af9" + integrity sha512-BRvdkwq5PAXBkjXjboO12uksDm3nrZEqDi4xF97Fk3Mnaa0zDOEfJa7hoKTY9b9KA1EkeWv9BL3i7hSd4SfGBg== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/context" "^0.7.0" @@ -160,7 +160,7 @@ dependencies: "@babel/types" "^7.21.5" -"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== @@ -977,7 +977,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== @@ -1037,118 +1037,11 @@ dependencies: "@date-io/core" "^1.3.13" -"@emotion/babel-plugin@^11.10.8": - version "11.10.8" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.8.tgz#bae325c902937665d00684038fd5294223ef9e1d" - integrity sha512-gxNky50AJL3AlkbjvTARiwAqei6/tNUxDZPSKd+3jqWVM3AmdVTTdpjHorR/an/M0VJqdsuq5oGcFH+rjtyujQ== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/serialize" "^1.1.1" - babel-plugin-macros "^3.1.0" - convert-source-map "^1.5.0" - escape-string-regexp "^4.0.0" - find-root "^1.1.0" - source-map "^0.5.7" - stylis "4.1.4" - -"@emotion/cache@^11.10.8": - version "11.10.8" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.8.tgz#3b39b4761bea0ae2f4f07f0a425eec8b6977c03e" - integrity sha512-5fyqGHi51LU95o7qQ/vD1jyvC4uCY5GcBT+UgP4LHdpO9jPDlXqhrRr9/wCKmfoAvh5G/F7aOh4MwQa+8uEqhA== - dependencies: - "@emotion/memoize" "^0.8.0" - "@emotion/sheet" "^1.2.1" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - stylis "4.1.4" - "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@emotion/hash@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" - integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== - -"@emotion/is-prop-valid@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83" - integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg== - dependencies: - "@emotion/memoize" "^0.8.0" - -"@emotion/memoize@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" - integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== - -"@emotion/react@^11.10.6": - version "11.10.8" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.8.tgz#02e274ecb45e03ab9d7a8eb9f0f0c064613eaf7b" - integrity sha512-ZfGfiABtJ1P1OXqOBsW08EgCDp5fK6C5I8hUJauc/VcJBGSzqAirMnFslhFWnZJ/w5HxPI36XbvMV0l4KZHl+w== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.8" - "@emotion/cache" "^11.10.8" - "@emotion/serialize" "^1.1.1" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - hoist-non-react-statics "^3.3.1" - -"@emotion/serialize@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0" - integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA== - dependencies: - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/unitless" "^0.8.0" - "@emotion/utils" "^1.2.0" - csstype "^3.0.2" - -"@emotion/sheet@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c" - integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA== - -"@emotion/styled@^11.10.6": - version "11.10.8" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.8.tgz#a3fd68efd90bd7e8a06b82b95adec643d386fa69" - integrity sha512-gow0lF4Uw/QEdX2REMhI8v6wLOabPKJ+4HKNF0xdJ2DJdznN6fxaXpQOx6sNkyBhSUL558Rmcu1Lq/MYlVo4vw== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.8" - "@emotion/is-prop-valid" "^1.2.0" - "@emotion/serialize" "^1.1.1" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@emotion/utils" "^1.2.0" - -"@emotion/unitless@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" - integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== - -"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" - integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== - -"@emotion/utils@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" - integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== - -"@emotion/weak-memoize@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" - integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== - "@eslint/eslintrc@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" @@ -1538,10 +1431,10 @@ resolved "https://registry.yarnpkg.com/@spinnaker/mocks/-/mocks-1.0.7.tgz#3e30fde4d691b310910e7fa5201c7424609042c0" integrity sha512-WXoNtZoFZFNDTrckCbbTShxoRN088IN+awWweNNv89EU4GsMX8HnnG/9J6N9rAt0R3ZgzhJmaNG06/JHHBzxEw== -"@spinnaker/pluginsdk-peerdeps@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@spinnaker/pluginsdk-peerdeps/-/pluginsdk-peerdeps-0.11.0.tgz#131eef2ed4c6eda32a6244681284eb89c827a125" - integrity sha512-tiTFXhlkAJ/yh1v8wl1odtcC7LrEr/q/qaTcH1uWnBz2q5laibIqac1Sc2C1C9UkSAcLXyhA88bcjdfGPiY3NQ== +"@spinnaker/pluginsdk-peerdeps@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@spinnaker/pluginsdk-peerdeps/-/pluginsdk-peerdeps-0.15.0.tgz#1e1b78e2d71018a86c17542c9c0c6f349162ad49" + integrity sha512-EnK79G8ntWaq/4tkaKXqVaz6EgCFlonYCAwntGHl4/mL9V1tA0/4IiCLaXBpZsWhVWDaGNTbIyAHAXFawgI7Tg== "@spinnaker/pluginsdk@0.3.0": version "0.3.0" @@ -1790,9 +1683,9 @@ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/node@*": - version "18.16.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" - integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== + version "20.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.0.0.tgz#081d9afd28421be956c1a47ced1c9a0034b467e2" + integrity sha512-cD2uPTDnQQCVpmRefonO98/PPijuOnnEy5oytWJFPY1N9aJCz2wJ5kSGWO+zJoed2cY2JxQh6yBuUq4vIn61hw== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1812,16 +1705,16 @@ "@types/react" "*" "@types/react-transition-group@^4.2.0": - version "4.4.5" - resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" - integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== + version "4.4.6" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.6.tgz#18187bcda5281f8e10dfc48f0943e2fdf4f75e2e" + integrity sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew== dependencies: "@types/react" "*" "@types/react@*": - version "18.2.0" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.0.tgz#15cda145354accfc09a18d2f2305f9fc099ada21" - integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA== + version "18.2.5" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.5.tgz#f9403e1113b12b53f7edcdd9a900c10dd4b49a59" + integrity sha512-RuoMedzJ5AOh23Dvws13LU9jpZHIc/k90AgmK7CecAYeWmSr3553L4u5rk4sWAPBuQosfT7HmTfG4Rg5o4nGEA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2002,16 +1895,16 @@ preact "~10.4.8" "@wry/context@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.0.tgz#be88e22c0ddf62aeb0ae9f95c3d90932c619a5c8" - integrity sha512-LcDAiYWRtwAoSOArfk7cuYvFXytxfVrdX7yxoUmK7pPITLk5jYh2F8knCwS7LjgYL8u1eidPlKKV6Ikqq0ODqQ== + version "0.7.2" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.2.tgz#732fa01cf11d08c07114ddf51d67c3757d68f31d" + integrity sha512-WBGObg2bxt9UYGX4Dh3heUpHeULiFIP/yLpKrcebPfwaLuwCSj6rS7kpQegQ/K7jbkTQ1nLGZnfyAvY1T2LG4g== dependencies: tslib "^2.3.0" "@wry/equality@^0.5.0": - version "0.5.3" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.3.tgz#fafebc69561aa2d40340da89fa7dc4b1f6fb7831" - integrity sha512-avR+UXdSrsF2v8vIqIgmeTY0UR91UT+IyablCyKe/uk22uOJ8fusKZnH9JH9e1/EtLeNJBtagNmL3eJdnOV53g== + version "0.5.5" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.5.tgz#523a15f670e6d15408bf8f67259682e18a5543f0" + integrity sha512-tI95+tJlL2LoOY27EHy0V0zKRVgbPp6vk9p6ZqWZOCSVslEhYEGeI+gaskc2rnjQxfszsXhtgYZTQ1xAUrMkOg== dependencies: tslib "^2.3.0" @@ -2270,15 +2163,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -babel-plugin-macros@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" - integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== - dependencies: - "@babel/runtime" "^7.12.5" - cosmiconfig "^7.0.0" - resolve "^1.19.0" - babel-plugin-polyfill-corejs2@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" @@ -2521,7 +2405,7 @@ cheerio@^1.0.0-rc.3: parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" -chokidar@^3.5.1: +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -2707,7 +2591,7 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.5.0, convert-source-map@^1.7.0: +convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -3192,9 +3076,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.284: - version "1.4.380" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.380.tgz#195dc59d930c6b74efbee6f0e6a267ce4af5ed91" - integrity sha512-XKGdI4pWM78eLH2cbXJHiBnWUwFSzZM7XujsB6stDiGu9AeSqziedP6amNLpJzE3i0rLTcfAwdCTs5ecP5yeSg== + version "1.4.384" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.384.tgz#5c23b5579930dec9af2a93edafddbe991542eace" + integrity sha512-I97q0MmRAAqj53+a8vZsDkEXBZki+ehYAOPzwtQzALip52aEp2+BJqHFtTlsfjoqVZYwPpHC8wM6MbsSZQ/Eqw== emoji-regex@^7.0.1: version "7.0.3" @@ -3392,11 +3276,6 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - eslint-config-prettier@6.12.0: version "6.12.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.12.0.tgz#9eb2bccff727db1c52104f0b49e87ea46605a0d2" @@ -3680,11 +3559,6 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" -find-root@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" - integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== - find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -4064,7 +3938,7 @@ hoist-non-react-statics@^2.5.5: resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== -hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -4208,6 +4082,11 @@ image-size@~0.5.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== +immutable@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" + integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== + import-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" @@ -6676,6 +6555,15 @@ safe-regex-test@^1.0.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sass@^1.62.1: + version "1.62.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.62.1.tgz#caa8d6bf098935bc92fc73fa169fb3790cacd029" + integrity sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + scheduler@^0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" @@ -6866,7 +6754,7 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -source-map-js@^1.0.2: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== @@ -6886,7 +6774,7 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.6, source-map@^0.5.7: +source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== @@ -7094,11 +6982,6 @@ stylehacks@^5.1.1: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" -stylis@4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b" - integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ== - supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"