diff --git a/client-reactjs/src/App.js b/client-reactjs/src/App.js index ee7ec971b..86c27c402 100644 --- a/client-reactjs/src/App.js +++ b/client-reactjs/src/App.js @@ -35,9 +35,13 @@ const ManualJobDetail = React.lazy(() => import('./components/application/Jobs/M const Actions = React.lazy(() => import('./components/application/actions/actions')); const AddJobsForm = React.lazy(() => import('./components/application/Jobs/AddjobsForm/AddJobsForm')); const FileMonitoring = React.lazy(() => import('./components/application/fileMonitoring/FileMonitoring')); +const OrbitMonitoring = React.lazy(() => import('./components/application/orbitMonitoring/OrbitMonitoring')); const SuperFileMonitoring = React.lazy(() => import('./components/application/superfileMonitoring/SuperFileMonitoring') ); +// const Notifications = React.lazy(() => import('./components/application/dashboard/notifications/Notifications')); +const Orbit = React.lazy(() => import('./components/application/dashboard/Orbit/Orbit')); +// const ClusterUsage = React.lazy(() => import('./components/application/dashboard/clusterUsage/ClusterUsage')); const Notifications = React.lazy(() => import('./components/application/dashboard/notifications')); const ClusterUsage = React.lazy(() => import('./components/application/dashboard/clusterUsage/')); const ClusterMonitoring = React.lazy(() => import('./components/application/clusterMonitoring')); @@ -53,6 +57,7 @@ const Regulations = React.lazy(() => import('./components/admin/ControlsAndRegul const GitHubSettings = React.lazy(() => import('./components/admin/GitHubSettings/GitHubSettings')); const ScheduledJobsPage = React.lazy(() => import('./components/admin/ScheduledJobsPage')); const Compliance = React.lazy(() => import('./components/admin/Compliance/Compliance')); +const Integrations = React.lazy(() => import('./components/admin/Integrations')); const TeamsNotification = React.lazy(() => import('./components/admin/notifications/MsTeams/Teams')); // Shared layout, etc. @@ -179,9 +184,11 @@ class App extends React.Component { + {' '} + @@ -206,6 +213,7 @@ class App extends React.Component { + { + const [integrations, setIntegrations] = useState([]); + const [modalVisible, setModalVisible] = useState(false); + const [modalWidth, setModalWidth] = useState(0); + const [confirmLoading, setConfirmLoading] = useState(false); + const [selectedIntegration, setSelectedIntegration] = useState({}); + const [notifications, setNotifications] = useState({}); + const [active, setActive] = useState(false); + const [notificationForm] = Form.useForm(); + const windowSize = useWindowSize(); + + // Changes modal size per screen vw + useEffect(() => { + const { width } = windowSize.inner; + if (width > 1500) { + setModalWidth('40vw'); + } else if (width > 1000) { + setModalWidth('60vw'); + } else { + setModalWidth('100vw'); + } + }, [windowSize]); + + const dispatch = useDispatch(); + const { + application: { applicationId }, + } = useSelector((state) => state.applicationReducer); + + useEffect(() => { + if (applicationId) getIntegrations(); + }, [applicationId]); + + const getIntegrations = async () => { + try { + const payload = { + method: 'GET', + header: authHeader(), + }; + const response = await fetch(`/api/integrations/get/${applicationId}`, payload); + + const data = await response.json(); + if (data) { + setIntegrations(data); + } + dispatch(applicationActions.getIntegrations(applicationId)); + } catch (err) { + console.log(err); + } + }; + + const editIntegration = async (record) => { + await setModalVisible(true); + await setSelectedIntegration(record); + await setNotifications(record.metaData); + }; + + const handleSave = async () => { + setConfirmLoading(true); + + const body = { notifications, active: { megaphoneActive: active } }; + + const payload = { + method: 'PUT', + header: authHeader(), + body: JSON.stringify(body), + }; + + const response = await fetch(`/api/integrations/update/${applicationId}/${selectedIntegration.name}`, payload); + + if (response.ok) { + getIntegrations(); + setConfirmLoading(false); + setModalVisible(false); + dispatch(applicationActions.getIntegrations(applicationId)); + + notificationForm.resetFields(); + message.success('Successfully updated Integration'); + } else { + message.success('An Error Occured, Integration not updated'); + } + }; + + const saveBtn = ( + + ); + const cancelModal = async () => { + setModalVisible(false); + }; + + const toggleIntegration = async (name) => { + try { + const payload = { + method: 'PUT', + header: authHeader(), + }; + const response = await fetch(`/api/integrations/toggle/${applicationId}/${name}`, payload); + + if (response.ok) { + getIntegrations(); + } + dispatch(applicationActions.getIntegrations(applicationId)); + } catch (err) { + console.log(err); + } + }; + + const columns = [ + { title: 'Name', dataIndex: 'name' }, + { title: 'Description', dataIndex: 'description' }, + { + title: 'Activate', + dataIndex: 'activate', + render: (_, record) => ( + + + + toggleIntegration(record.name)} /> + + + + ), + }, + { + title: 'Edit', + dataIndex: 'edit', + render: (_, record) => ( + + + editIntegration(record)} /> + + + ), + }, + ]; + + return ( + <> + +
+ record.id} /> + +
+

Megaphone Notification Settings

+ + { + setActive(e); + }}> + + + setNotifications({ ...notifications, notificationEmails: e.target.value })}> + + + setNotifications({ ...notifications, notificationWebhooks: e.target.value })}> + + + setNotifications({ ...notifications, notificationEmailsSev3: e.target.value })}> + + +
+ + ); +}; + +export default Integrations; diff --git a/client-reactjs/src/components/application/dashboard/Orbit/Filters.jsx b/client-reactjs/src/components/application/dashboard/Orbit/Filters.jsx new file mode 100644 index 000000000..274d2854d --- /dev/null +++ b/client-reactjs/src/components/application/dashboard/Orbit/Filters.jsx @@ -0,0 +1,336 @@ +import React, { useState } from 'react'; +import { Form, Select, DatePicker, Button, Checkbox, Drawer } from 'antd'; +import { useHistory } from 'react-router-dom'; +import { handleError } from '../../../common/AuthHeader.js'; +import '../common/css/index.css'; +import { FilterOutlined } from '@ant-design/icons'; + +// Form layout +const layout = { + labelCol: { span: 24 }, + wrapperCol: { span: 24 }, +}; +function Filters({ groupDataBy, setGroupDataBy, dashboardFilters, setDashboardFilters }) { + const [form] = Form.useForm(); + const history = useHistory(); + const [open, setOpen] = useState(false); + + const showDrawer = () => { + setOpen(true); + }; + + const onClose = () => { + setOpen(false); + }; + + // When form is submitted + const onFinish = () => { + try { + form.validateFields(); + } catch (err) { + handleError(err); + } + }; + + // Disable future dates + const disabledDate = (current) => { + const date = new Date(); + const todaysDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1); + return current && current >= todaysDate; + }; + + // When filters are changed - add to url params to persist on page refresh + const updateParams = (param) => { + const newParams = new URLSearchParams(); + const allFilters = { ...dashboardFilters, ...param }; + const allowedURLParams = ['initialStatus', 'finalStatus', 'severity', 'dateRange', 'groupDataBy']; + for (let key in allFilters) { + //only allow certain params to be added to url + if (allowedURLParams.includes(key)) { + newParams.set(key, allFilters[key]); + } + } + history.push(`?${newParams.toString()}`); + }; + + //function to select or clear all when select all boxes are checked + const selectAll = (e, option) => { + //concatenate so we can dyanmically call the correct state variable to avoid case/switch + const optionConcat = option + 'Options'; + + if (e.target.checked) { + form.setFieldsValue({ [option]: dashboardFilters[optionConcat].map((option) => option.value) }); + setDashboardFilters((prev) => ({ + ...prev, + [option]: dashboardFilters[optionConcat].map((option) => option.value), + })); + updateParams({ [option]: dashboardFilters[optionConcat].map((option) => option.value) }); + } else { + form.setFieldsValue({ [option]: [] }); + setDashboardFilters((prev) => ({ ...prev, [option]: [] })); + updateParams({ [option]: [] }); + } + }; + + return ( + <> + + +
+
+

Workunit Filters

+ + { + setDashboardFilters((prev) => ({ ...prev, finalStatus: values })); + updateParams({ finalStatus: values }); + }} + dropdownRender={(menu) => ( + <> +
+ selectAll(e, 'finalStatus')}> + Select All + + + {menu} +
+ + )} + /> +
+ + + + + + + + + +

Build Filters

+ + + + + + + + + + + + + +

Chart Slicers

+ + + { + setDashboardFilters((prev) => ({ ...prev, dateRange: value })); + updateParams({ dateRange: value }); + const numberOfDays = Math.ceil(Math.abs(value[0] - value[1]) / (1000 * 60 * 60 * 24) + 1); + let suggestedFilterByOption; + if (numberOfDays <= 15) { + setGroupDataBy('day'); + suggestedFilterByOption = 'day'; + } else if (numberOfDays > 15 && numberOfDays <= 105) { + setGroupDataBy('week'); + suggestedFilterByOption = 'week'; + } else if (numberOfDays > 105 && numberOfDays <= 548) { + setGroupDataBy('month'); + suggestedFilterByOption = 'month'; + } else { + suggestedFilterByOption = 'year'; + setGroupDataBy('year'); + } + form.setFieldsValue({ groupDataBy: suggestedFilterByOption }); + }} + /> + + + + + + + + + + +
+
+ + ); +} +export default Filters; diff --git a/client-reactjs/src/components/application/dashboard/Orbit/Orbit.jsx b/client-reactjs/src/components/application/dashboard/Orbit/Orbit.jsx new file mode 100644 index 000000000..df8a70c96 --- /dev/null +++ b/client-reactjs/src/components/application/dashboard/Orbit/Orbit.jsx @@ -0,0 +1,497 @@ +import React, { useEffect, useState } from 'react'; +import { Tabs, Empty, Spin, Space } from 'antd'; +import { useSelector } from 'react-redux'; +import moment from 'moment'; +import OrbitTable from './OrbitTable'; +import WorkUnitCharts from '../common/charts/WorkUnitCharts'; +import Filters from './Filters'; +import MetricBoxes from '../common/charts/MetricBoxes'; +import '../common/css/index.css'; +import ExportMenu from '../ExportMenu/ExportMenu'; +import { authHeader, handleError } from '../../../common/AuthHeader.js'; +import { message } from 'antd'; + +function Orbit() { + //all states needed to manage data + const [builds, setBuilds] = useState([]); + const [workUnits, setWorkUnits] = useState([]); + const [filteredWorkUnits, setFilteredWorkUnits] = useState(null); + const [filteredBuilds, setFilteredBuilds] = useState(null); + + //states for charts and titles + const [titleMetrics, setTitleMetrics] = useState([]); + const [metrics, setMetrics] = useState([]); + const [stackBarData, setStackBarData] = useState([]); + const [donutData, setDonutData] = useState([]); + const [groupDataBy, setGroupDataBy] = useState('day'); + + //states for filters -- place values initially to avoid load errors + const [dashboardFilters, setDashboardFilters] = useState({ + initialStatus: [], + initialStatusOptions: [], + finalStatus: [], + finalStatusOptions: [], + version: [], + versionOptions: [], + severity: [], + severityOptions: [], + builds: [], + buildsOptions: [], + products: [], + productsOptions: [], + businessUnits: [], + businessUnitsOptions: [], + wuid: [], + wuidOptions: [], + dateRange: [moment().subtract(15, 'days'), moment()], + groupDataBy: 'day', + }); + + //states for spinners + const [loading, setLoading] = useState(false); + + //grab the application ID from redux + const { + application: { applicationId }, + } = useSelector((item) => item.applicationReducer); + + // Step 1 - first we are loading the initial builds and workunits into state + useEffect(() => { + getbuilds(); + }, [applicationId]); + + //Get list of all builds, workunits, and set initial values and filters + const getbuilds = async () => { + try { + setLoading(true); + const payload = { + method: 'GET', + header: authHeader(), + }; + + if (applicationId === undefined) return; + + const response = await fetch(`/api/orbit/allMonitoring/${applicationId}`, payload); + if (!response.ok) { + setLoading(false); + handleError(response); + } + const data = await response.json(); + + //get work unit information and put it in builds information + const response2 = await fetch(`/api/orbit/getWorkUnits/${applicationId}`, payload); + if (!response2.ok) { + setLoading(false); + handleError(response2); + } + const data2 = await response2.json(); + + let builds2 = []; + + await Promise.all( + //add data2 workunits to matching builds + (builds2 = data.map((build) => { + const wu = data2.filter((workUnit) => workUnit.name === build.build); + return { ...build, workUnits: wu }; + })) + ); + + //combine and set work units + let totalWuList = data2; + + totalWuList.sort((a, b) => { + return new Date(b.metaData.lastRun) - new Date(a.metaData.lastRun); + }); + + //move stuff out of metaData to top level to make it easier to work with + totalWuList.forEach((workUnit) => { + workUnit.key = workUnit.id; + workUnit.initialStatus = workUnit.metaData.initialStatus; + workUnit.finalStatus = workUnit.metaData.finalStatus; + workUnit.version = workUnit.metaData.version; + workUnit.status = workUnit.metaData.status; + workUnit.lastRun = workUnit.metaData.lastRun; + }); + + //get unique values from workunit list + const uniqueInitialStatus = [...new Set(totalWuList.map((item) => item.initialStatus.toUpperCase()))]; + const uniqueFinalStatus = [...new Set(totalWuList.map((item) => item.finalStatus.toUpperCase()))]; + const uniqueVersion = [...new Set(totalWuList.map((item) => item.version))]; + const uniqueWorkUnits = [...new Set(totalWuList.map((item) => item.wuid))]; + + //get unique values from builds list + const uniqueSeverity = [...new Set(builds2.map((item) => item.severityCode))]; + const uniqueBuilds = [...new Set(builds2.map((item) => item.build))]; + const uniqueProducts = [...new Set(builds2.map((item) => item.product))]; + const uniqueBusinessUnits = [...new Set(builds2.map((item) => item.businessUnit))]; + + //create options for dropdowns + const uniqueInitialStatusOptions = []; + uniqueInitialStatus.forEach((item) => uniqueInitialStatusOptions.push({ label: item, value: item })); + const uniqueFinalStatusOptions = []; + uniqueFinalStatus.forEach((item) => uniqueFinalStatusOptions.push({ label: item, value: item })); + const uniqueVersionOptions = []; + uniqueVersion.forEach((item) => uniqueVersionOptions.push({ label: item, value: item })); + const uniqueSeverityOptions = []; + uniqueSeverity.forEach((item) => uniqueSeverityOptions.push({ label: item, value: item })); + const uniqueBuildsOptions = []; + uniqueBuilds.forEach((item) => uniqueBuildsOptions.push({ label: item, value: item })); + const uniqueProductsOptions = []; + uniqueProducts.forEach((item) => uniqueProductsOptions.push({ label: item, value: item })); + const uniqueBusinessUnitsOptions = []; + uniqueBusinessUnits.forEach((item) => uniqueBusinessUnitsOptions.push({ label: item, value: item })); + const uniqueWorkUnitsOptions = []; + uniqueWorkUnits.forEach((item) => uniqueWorkUnitsOptions.push({ label: item, value: item })); + + //check URL params for filters + const params = new URLSearchParams(location.search); + const filters = {}; + if (params.get('initialStatus')) { + filters.initialStatus = params.get('initialStatus')?.split(','); + } + if (params.get('finalStatus')) { + filters.finalStatus = params.get('finalStatus')?.split(','); + } + if (params.get('dateRange')) { + const dateString = params.get('dateRange'); + const dates = dateString.split(','); + const range = [moment(dates[0]), moment(dates[1])]; + filters.dateRange = range; + } + if (params.get('groupDataBy')) { + filters.groupDataBy = params.get('groupDataBy'); + setGroupDataBy(params.get('groupDataBy')); + // setGroupDataBy('day'); + } + + setWorkUnits(totalWuList); + + setBuilds(builds2); + + //set them in state for the initial load + setDashboardFilters((dashboardFilters) => ({ + ...dashboardFilters, + initialStatus: filters.initialStatus?.length ? filters.initialStatus : uniqueInitialStatus, + initialStatusOptions: uniqueInitialStatusOptions, + finalStatus: filters.finalStatus?.length ? filters.finalStatus : uniqueFinalStatus, + finalStatusOptions: uniqueFinalStatusOptions, + version: uniqueVersion, + versionOptions: uniqueVersionOptions, + severity: uniqueSeverity, + severityOptions: uniqueSeverityOptions, + builds: uniqueBuilds, + buildsOptions: uniqueBuildsOptions, + products: uniqueProducts, + productsOptions: uniqueProductsOptions, + businessUnits: uniqueBusinessUnits, + businessUnitsOptions: uniqueBusinessUnitsOptions, + wuid: uniqueWorkUnits, + wuidOptions: uniqueWorkUnitsOptions, + dateRange: filters.dateRange?.length ? filters.dateRange : [moment().subtract(15, 'days'), moment()], + groupDataBy: filters.groupDataBy ? filters.groupDataBy : 'day', + })); + + setLoading(false); + } catch (error) { + message.error('Failed to fetch builds' + error); + console.log(error); + } + }; + + // Step 2 - now we need to apply the initial filters to the initial data + useEffect(() => { + //if filtered builds and workunits are empty and the initial data and filters are set, filter data + if ( + filteredBuilds === null && + filteredWorkUnits === null && + builds.length > 0 && + workUnits.length > 0 && + dashboardFilters?.initialStatus?.length > 0 + ) { + filterData(); + } + }, [builds, workUnits, dashboardFilters]); + + const filterData = () => { + let filteredBuildsList = []; + + if (builds.length === 0 || workUnits.length === 0) return; + //apply filters to build list + builds.forEach((build) => { + if ( + dashboardFilters.severity.includes(build.severityCode) && + dashboardFilters.builds.includes(build.build) && + dashboardFilters.products.includes(build.product) && + dashboardFilters.businessUnits.includes(build.businessUnit) + ) { + filteredBuildsList.push(build); + } else { + return; + } + }); + + setFilteredBuilds(filteredBuildsList); + + //get a list of just the names to filter work units + let filteredBuildNameList = filteredBuildsList.map((build) => build.build); + + //apply filters to work units once the build list is filtered + let filtered = workUnits.filter((workUnit) => { + let wuDate = moment(workUnit.lastRun); + + if ( + wuDate > moment(dashboardFilters.dateRange[0]) && + wuDate < moment(dashboardFilters.dateRange[1]) && + dashboardFilters.initialStatus?.includes(workUnit.initialStatus.toUpperCase()) && + dashboardFilters.finalStatus?.includes(workUnit.finalStatus.toUpperCase()) && + dashboardFilters.version?.includes(workUnit.version) && + filteredBuildNameList.includes(workUnit.name) + ) { + return true; + } else { + return false; + } + }); + + setFilteredWorkUnits(filtered); + }; + + //Step 3 - now that the filtered builds and workunits are set, we can get the counts and chart data + useEffect(() => { + if (filteredBuilds !== null && filteredWorkUnits !== null && filteredBuilds[0].count === undefined) { + getCounts(); + setChartData(); + } + }, [filteredBuilds, filteredWorkUnits]); + + const getCounts = () => { + let builds2 = []; + let wus = []; + + filteredBuilds.forEach((build) => { + let count = 0; + + if (build.workUnits?.length > 0) { + build.workUnits.forEach((workUnit) => { + let wuDate = moment(workUnit.lastRun); + + if ( + wuDate > moment(dashboardFilters.dateRange[0]) && + wuDate < moment(dashboardFilters?.dateRange[1]) && + dashboardFilters.initialStatus?.includes(workUnit.initialStatus.toUpperCase()) && + dashboardFilters.finalStatus?.includes(workUnit.finalStatus.toUpperCase()) && + dashboardFilters.version?.includes(workUnit.version) && + dashboardFilters.builds?.includes(workUnit.name) + ) { + //check if workunit already exists in wus list to avoid duplicates + let wuExists = wus.find((item) => item.id === workUnit.id); + if (!wuExists) { + wus.push(workUnit); + count++; + } + } + }); + } + + builds2.push({ ...build, count: count }); + }); + + setFilteredBuilds(builds2); + setLoading(false); + }; + + const setChartData = () => { + if (filteredWorkUnits) { + const newMetrics = []; // Pie data + const newStackBarData = []; // Stack bar Data + const newDonutData = []; // Donut data + const newTitleMetrics = []; //title metrics + + newTitleMetrics.push({ title: 'Work Units', description: filteredWorkUnits.length }); + newTitleMetrics.push({ + title: 'Builds', + description: filteredBuilds.length > 0 ? filteredBuilds.length : builds.length, + }); + //get date range from filters + if (dashboardFilters?.dateRange) { + newTitleMetrics.push({ + title: 'Date Range Selected', + description: + moment(dashboardFilters?.dateRange[0]).format('MM/DD/YY') + + ' - ' + + moment(dashboardFilters?.dateRange[1]).format('MM/DD/YY'), + }); + } + + const workUnitCountByInitialStatus = {}; + const workUnitCountByFinalStatus = {}; + const workUnitCountByBuild = {}; + + let data; + switch (groupDataBy) { + case 'week': + data = filteredWorkUnits.map((workUnit) => { + const weekStart = moment(workUnit.lastRun).startOf('week').format('MM/DD/YY'); + const weekEnd = moment(workUnit.lastRun).endOf('week').format('MM/DD/YY'); + const updatedItem = { ...workUnit }; + updatedItem.metaData.lastRun = `${weekStart} - ${weekEnd}`; + return updatedItem; + }); + break; + + case 'month': + data = filteredWorkUnits.map((workUnit) => { + const updatedItem = { ...workUnit }; + updatedItem.metaData.lastRun = moment(moment(workUnit.lastRun).utc(), 'MM/DD/YYYY').format('MMMM YYYY'); + return updatedItem; + }); + break; + + case 'quarter': + data = filteredWorkUnits.map((workUnit) => { + const updatedItem = { ...workUnit }; + const date = moment.utc(workUnit.lastRun); + const year = date.year(); + const quarter = Math.ceil((date.month() + 1) / 3); + updatedItem.metaData.lastRun = `${year} - Q${quarter}`; + return updatedItem; + }); + break; + + case 'year': + data = filteredWorkUnits.map((workUnit) => { + const updatedItem = { ...workUnit }; + const date = moment.utc(workUnit.lastRun); + const year = date.year(); + updatedItem.metaData.lastRun = year; + return updatedItem; + }); + break; + + default: + data = filteredWorkUnits; + } + //--------------------------------------- + data.forEach((workUnit) => { + if (workUnitCountByInitialStatus[workUnit?.initialStatus]) { + const newCountInitial = workUnitCountByInitialStatus[workUnit.initialStatus] + 1; + workUnitCountByInitialStatus[workUnit.initialStatus] = newCountInitial; + } else { + workUnitCountByInitialStatus[workUnit?.initialStatus] = 1; + } + + if (workUnitCountByFinalStatus[workUnit?.finalStatus]) { + const newCountFinal = workUnitCountByFinalStatus[workUnit.finalStatus] + 1; + workUnitCountByFinalStatus[workUnit.finalStatus] = newCountFinal; + } else { + workUnitCountByFinalStatus[workUnit?.finalStatus] = 1; + } + + if (groupDataBy == 'day') { + newStackBarData.push({ + x: workUnit.metaData.lastRun.split('T')[0], + y: 1, + z: workUnit.finalStatus, + }); + } else { + newStackBarData.push({ x: workUnit.metaData.lastRun, y: 1, z: workUnit?.finalStatus }); + } + + // workUnitCountByBuild; + const { Build } = workUnit; + if (workUnitCountByBuild[Build]) { + workUnitCountByBuild[Build] = workUnitCountByBuild[Build] + 1; + } else { + workUnitCountByBuild[Build] = 1; + } + }); + + //--------------------------------------- + for (let key in workUnitCountByFinalStatus) { + newMetrics.push({ type: key, value: workUnitCountByFinalStatus[key] }); + } + //--------------------------------------- + for (let key in workUnitCountByInitialStatus) { + newDonutData.push({ type: key, value: workUnitCountByInitialStatus?.[key] }); + } + //--------------------------------------- + + //add titles + newStackBarData.push({ title: 'Count of Workunits by Date and Final Build Status' }); + newDonutData.push({ title: 'Count of Workunits by Initial Build Status' }); + newMetrics.push({ title: 'Count of Workunits by Final Build Status' }); + + setTitleMetrics(newTitleMetrics); + setMetrics(newMetrics); + setStackBarData(newStackBarData); + setDonutData(newDonutData); + } + }; + + //Step 4 (repeatable) - now we need to re-render the data when it filters change + useEffect(() => { + //if dashboard filters are set and filtered builds and workunits + if (Object.keys(dashboardFilters).length && filteredBuilds !== null && filteredWorkUnits !== null) { + filterData(); + } + }, [dashboardFilters]); + + return ( +
+ + + + + + + + + }> + +
+
+ +
+
+ + + {builds.length > 0 ? ( +
+ +
+ ) : loading ? ( +
+ +
+ ) : ( + + )} +
+
+
+
+ ); +} + +export default Orbit; diff --git a/client-reactjs/src/components/application/dashboard/Orbit/OrbitTable.jsx b/client-reactjs/src/components/application/dashboard/Orbit/OrbitTable.jsx new file mode 100644 index 000000000..54dc863c6 --- /dev/null +++ b/client-reactjs/src/components/application/dashboard/Orbit/OrbitTable.jsx @@ -0,0 +1,77 @@ +/* eslint-disable unused-imports/no-unused-vars */ +import React from 'react'; +import { Table } from 'antd'; + +function OrbitTable({ filteredWorkUnits, filteredBuilds, loading }) { + //Table columns and data + const columns = [ + { + title: 'Product', + render: (record) => { + return record.product?.toUpperCase(); + }, + width: 225, + }, + { title: 'Orbit Build Name', dataIndex: 'build' }, + { + title: 'WUs', + render: (record) => { + return record?.count ? record.count : 0; + }, + sorter: (a, b) => a.count - b.count, + + sortDirections: ['ascend', 'descend'], + defaultSortOrder: 'descend', + width: 75, + }, + ]; + + const wuColumns = [ + { title: 'Build WUID', dataIndex: 'wuid', width: 75 }, + { title: 'Version', dataIndex: 'version', width: 75 }, + { title: 'Initial Status', dataIndex: 'initialStatus', width: 100 }, + { title: 'Final Status', dataIndex: 'finalStatus', width: 150 }, + { title: 'Build Owner', dataIndex: 'primaryContact', width: 150 }, + ]; + + //JSX + return ( + <> +
+
+
record.name} + verticalAlign="top" + loading={loading} + pagination={false} + headerColor="white" + headerBg="#001529" + scroll={{ y: 400 }} + /> + +
+
+
record.key} + verticalAlign="top" + loading={loading} + headerColor="white" + headerBg="#001529" + scroll={{ x: 1300, y: 400 }} + pagination={false} + /> + + + + ); +} + +export default OrbitTable; diff --git a/client-reactjs/src/components/application/dashboard/notifications/charts/Donut.jsx b/client-reactjs/src/components/application/dashboard/common/charts/Donut.jsx similarity index 100% rename from client-reactjs/src/components/application/dashboard/notifications/charts/Donut.jsx rename to client-reactjs/src/components/application/dashboard/common/charts/Donut.jsx diff --git a/client-reactjs/src/components/application/dashboard/common/charts/MetricBoxes.jsx b/client-reactjs/src/components/application/dashboard/common/charts/MetricBoxes.jsx new file mode 100644 index 000000000..e50e24a54 --- /dev/null +++ b/client-reactjs/src/components/application/dashboard/common/charts/MetricBoxes.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Card } from 'antd'; +import _ from 'lodash'; + +function MetricBoxes({ metrics, bordered, headStyle }) { + return ( +
+ {metrics.map((metric) => { + const { title } = metric; + return ( + + {metric.description} + + ); + })} +
+ ); +} + +export default MetricBoxes; diff --git a/client-reactjs/src/components/application/dashboard/notifications/charts/NotificationCharts.jsx b/client-reactjs/src/components/application/dashboard/common/charts/NotificationCharts.jsx similarity index 100% rename from client-reactjs/src/components/application/dashboard/notifications/charts/NotificationCharts.jsx rename to client-reactjs/src/components/application/dashboard/common/charts/NotificationCharts.jsx diff --git a/client-reactjs/src/components/application/dashboard/notifications/charts/Pie.jsx b/client-reactjs/src/components/application/dashboard/common/charts/Pie.jsx similarity index 100% rename from client-reactjs/src/components/application/dashboard/notifications/charts/Pie.jsx rename to client-reactjs/src/components/application/dashboard/common/charts/Pie.jsx diff --git a/client-reactjs/src/components/application/dashboard/notifications/charts/StackedBar.jsx b/client-reactjs/src/components/application/dashboard/common/charts/StackedBar.jsx similarity index 100% rename from client-reactjs/src/components/application/dashboard/notifications/charts/StackedBar.jsx rename to client-reactjs/src/components/application/dashboard/common/charts/StackedBar.jsx diff --git a/client-reactjs/src/components/application/dashboard/common/charts/WorkUnitCharts.jsx b/client-reactjs/src/components/application/dashboard/common/charts/WorkUnitCharts.jsx new file mode 100644 index 000000000..d5c571b12 --- /dev/null +++ b/client-reactjs/src/components/application/dashboard/common/charts/WorkUnitCharts.jsx @@ -0,0 +1,35 @@ +/* eslint-disable prettier/prettier */ +import React from 'react'; +import { Resizable } from 're-resizable'; +// import Pie from './Pie'; +import StackedBar from './StackedBar'; +import Donut from './Donut'; + +function WorkUnitCharts({ metrics, stackBarData, groupDataBy, donutData }) { + return ( +
+ +
+

{donutData[donutData.length - 1]?.title}

+ {} +
+
+ + +
+

{metrics[metrics.length - 1]?.title}

+ {}{' '} +
+
+ + +
+

{stackBarData[stackBarData.length - 1]?.title}

+ {} +
+
+
+ ); +} + +export default WorkUnitCharts; diff --git a/client-reactjs/src/components/application/dashboard/notifications/index.css b/client-reactjs/src/components/application/dashboard/common/css/index.css similarity index 100% rename from client-reactjs/src/components/application/dashboard/notifications/index.css rename to client-reactjs/src/components/application/dashboard/common/css/index.css diff --git a/client-reactjs/src/components/application/dashboard/notifications/monitoringStatusOptions.js b/client-reactjs/src/components/application/dashboard/common/monitoringStatusOptions.js similarity index 100% rename from client-reactjs/src/components/application/dashboard/notifications/monitoringStatusOptions.js rename to client-reactjs/src/components/application/dashboard/common/monitoringStatusOptions.js diff --git a/client-reactjs/src/components/application/dashboard/notifications/BulkActions.jsx b/client-reactjs/src/components/application/dashboard/notifications/BulkActions.jsx index 77de33c68..e3e6417fc 100644 --- a/client-reactjs/src/components/application/dashboard/notifications/BulkActions.jsx +++ b/client-reactjs/src/components/application/dashboard/notifications/BulkActions.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { Modal, Select, Form, Button, message, Input } from 'antd'; import { authHeader } from '../../../common/AuthHeader'; -import { monitoringStatusOptions } from './monitoringStatusOptions.js'; +import { monitoringStatusOptions } from '../common/monitoringStatusOptions.js'; import { v4 as uuidv4 } from 'uuid'; import { useForm } from 'antd/lib/form/Form'; diff --git a/client-reactjs/src/components/application/dashboard/notifications/charts/Filters.jsx b/client-reactjs/src/components/application/dashboard/notifications/Filters.jsx similarity index 84% rename from client-reactjs/src/components/application/dashboard/notifications/charts/Filters.jsx rename to client-reactjs/src/components/application/dashboard/notifications/Filters.jsx index 092d649d8..9a1dccf4d 100644 --- a/client-reactjs/src/components/application/dashboard/notifications/charts/Filters.jsx +++ b/client-reactjs/src/components/application/dashboard/notifications/Filters.jsx @@ -3,16 +3,18 @@ import { Form, Select, DatePicker, Button, message } from 'antd'; import { useHistory, useLocation } from 'react-router-dom'; import moment from 'moment'; -import { authHeader, handleError } from '../../../../common/AuthHeader.js'; -import { monitoringStatusOptions } from '../monitoringStatusOptions.js'; -import '../index.css'; +import { authHeader, handleError } from '../../../common/AuthHeader.js'; +import { monitoringStatusOptions } from '../common/monitoringStatusOptions.js'; +import '../common/css/index.css'; -//Monitoring types options -const monitoringTypeOptions = [ +//Monitoring types options for notifications +const monitoringTypeOptionsForNotifications = [ { label: 'Job', value: 'jobMonitoring' }, { label: 'File', value: 'file' }, { label: 'Cluster', value: 'cluster' }, { label: 'Super File', value: 'superFile' }, + { label: 'Megaphone', value: 'megaphone' }, + { label: 'Orbit Monitoring', value: 'orbitMonitoring' }, ]; // Group by options @@ -30,11 +32,19 @@ const layout = { wrapperCol: { span: 24 }, }; -function Filters({ applicationId, setNotifications, setLoadingData, groupDataBy, setGroupDataBy, setDefaultFilters }) { +function Filters({ + applicationId, + setNotifications, + setLoadingData, + groupDataBy, + setGroupDataBy, + setDefaultFilters, + isOrbit, +}) { const [form] = Form.useForm(); const [dashboardFilters, setDashboardFilters] = useState({}); const initialValues = { - monitoringType: ['jobMonitoring', 'file', 'cluster', 'superFile'], + monitoringType: ['jobMonitoring', 'file', 'cluster', 'superFile', 'megaphone', 'orbitMonitoring'], monitoringStatus: ['notified', 'triage', 'inProgress', 'completed'], dateRange: [moment().subtract(15, 'days'), moment()], groupDataBy: groupDataBy, @@ -123,17 +133,20 @@ function Filters({ applicationId, setNotifications, setLoadingData, groupDataBy, return (
- - { + setDashboardFilters((prev) => ({ ...prev, monitoringType: values })); + updateParams({ monitoringType: values }); + }} + /> + + + ) : null} { + setMonitoringDetails({ + ...monitoringDetails, + businessUnit: value, + }); + }}> + + + + + + + + + + + +
+ loadOrbitBuildSuggestions(searchText)} + value={selectedOrbitBuild} + status={loading ? 'warning' : null}> + + + + + + {orbitBuildDetails ? ( + <> + {displayBuildInfo ? ( + <> +
+ Build Details +
+
+ +
+ + ) : null} + + ) : null} + + ); +}; + +export default BasicTab; diff --git a/client-reactjs/src/components/application/orbitMonitoring/MonitoringTab.jsx b/client-reactjs/src/components/application/orbitMonitoring/MonitoringTab.jsx new file mode 100644 index 000000000..2d75796a1 --- /dev/null +++ b/client-reactjs/src/components/application/orbitMonitoring/MonitoringTab.jsx @@ -0,0 +1,312 @@ +import React, { useState, useEffect } from 'react'; +import { Form, Input, Typography, Checkbox, Select } from 'antd'; +import cronstrue from 'cronstrue'; +import InfoDrawer from '../../common/InfoDrawer'; +import { InfoCircleOutlined } from '@ant-design/icons'; + +const { Title, Text } = Typography; + +const MonitoringTab = ({ + entryForm, + cron, + setCron, + monitoringDetails, + setMonitoringDetails, + selectedOrbitBuildDetails, + orbitBuildList, + editing, +}) => { + const [cronExplainer, setCronExplainer] = useState(null); + const [updateInterval, setUpdateInterval] = useState(null); + const [updateIntervalDays, setUpdateIntervalDays] = useState(null); + const [buildStatus, setBuildStatus] = useState(null); + const [severityCode, setSeverityCode] = useState(null); + const [required, setRequired] = useState(true); + const [open, setOpen] = useState(false); + + //TODO, try and get rid of this flag + const [updatedLocals, setUpdatedLocals] = useState(false); + + //update required state if update interval or update interval days is changed, use this to validate that one or the other is filled at least + useEffect(() => { + const result = updateInterval || updateIntervalDays; + setRequired(result ? false : true); + + if (editing && selectedOrbitBuildDetails && !updateInterval && !updateIntervalDays && !updatedLocals) { + setUpdateInterval(selectedOrbitBuildDetails?.metaData?.monitoringCondition?.updateInterval || null); + setUpdateIntervalDays(selectedOrbitBuildDetails?.metaData?.monitoringCondition?.updateIntervalDays || null); + //set this flag so it won't try and reset locals again and again preventing fields from being disabled + setUpdatedLocals(true); + } + entryForm.validateFields(['updateInterval', 'updateIntervalDays']); + }, [updateInterval, updateIntervalDays]); + + const showDrawer = () => { + setOpen(true); + }; + + const onClose = () => { + setOpen(false); + }; + + useEffect(() => { + entryForm.setFieldsValue({ + isActive: monitoringDetails.isActive, + }); + }, []); + + const notificationConditions = [ + { label: 'Build Status', value: 'buildStatus' }, + { label: 'Build Interval', value: 'updateInterval' }, + ]; + + const severityCodes = [ + { label: '0', value: 0 }, + { label: '1', value: 1 }, + { label: '2', value: 2 }, + { label: '3', value: 3 }, + ]; + + const daysOfTheWeek = [ + { label: 'Monday', value: 'monday' }, + { label: 'Tuesday', value: 'tuesday' }, + { label: 'Wednesday', value: 'wednesday' }, + { label: 'Thursday', value: 'thursday' }, + { label: 'Friday', value: 'friday' }, + { label: 'Saturday', value: 'saturday' }, + { label: 'Sunday', value: 'sunday' }, + ]; + + const buildStatuses = [ + { label: 'BUILD_AVAILABLE_FOR_USE', value: 'build_available_for_use' }, + { label: 'DISCARDED', value: 'discarded' }, + { label: 'FAILED_QA_QAHELD', value: 'failed_qa_qaheld' }, + { label: 'GRAVEYARD', value: 'graveyard' }, + { label: 'PASSED_QA', value: 'passed_qa' }, + { label: 'PASSED_QA_NO_RELEASE', value: 'passed_qa_no_release' }, + { label: 'PRODUCTION', value: 'production' }, + { label: 'SKIPPED', value: 'skipped' }, + ]; + + useEffect(() => { + if (cron) { + try { + const explainer = cronstrue.toString(cron); + entryForm.setFields([ + { + name: 'cron', + errors: null, + }, + ]); + setCronExplainer({ valid: true, message: `Runs ${explainer.toLocaleLowerCase()}` }); + } catch (err) { + setCronExplainer(null); + entryForm.setFields([ + { + name: 'cron', + errors: [err.split(':')[1]], + }, + ]); + } + } + if (!cron) setCronExplainer(null); + }, [cron]); + + return ( + <> + { + // only validate if not viewing other file details + if (editing) { + return Promise.resolve(); + } else { + const nameExists = orbitBuildList.find((Monitoring) => Monitoring.name === value); + + // if (nameExists || selectedOrbitBuildMonitoringDetails) { + if (!nameExists) { + return Promise.resolve(); + } else { + return Promise.reject(); + } + } + }, + }, + ]}> + + + +

+ Cron (How often to monitor) + showDrawer()} /> +

+ + + } + style={{ width: 'calc(47.5% - 8px)' }} + onChange={(e) => setMonitoringDetails({ ...monitoringDetails, cron: e.target.value })} + name="cron" + rules={[ + { required: true, message: 'Required field' }, + { + validator: async (_, cron) => { + if (cron) { + const cronInArray = cron.split(' '); + if (cronInArray.length > 5) { + setCronExplainer({ valid: false, message: `` }); + return Promise.reject('Cron expression has more than 5 parts'); + } else { + cronstrue.toString(cron); + } + } + }, + }, + ]} + extra={ + cronExplainer ? ( + {cronExplainer.message} + ) : ( + + Click here to create cron expression + + ) + }> + { + setCron(e.target.value); + }} + /> +
+ + + + + + + + + + {monitoringDetails.monitoringConditions.includes('updateInterval') ? ( +
+ + <Text type="danger">*</Text> Build Interval: (choose one) + +
+ + { + setUpdateInterval(e.target.value); + }}> + + + + + +
+
+ ) : null} + + {monitoringDetails.monitoringConditions.includes('buildStatus') ? ( + <> + + + + + ) : null} + + + Start monitoring now + + + ); +}; + +export default MonitoringTab; diff --git a/client-reactjs/src/components/application/orbitMonitoring/NotificationsTab.jsx b/client-reactjs/src/components/application/orbitMonitoring/NotificationsTab.jsx new file mode 100644 index 000000000..9b5b06254 --- /dev/null +++ b/client-reactjs/src/components/application/orbitMonitoring/NotificationsTab.jsx @@ -0,0 +1,162 @@ +import React, { useState } from 'react'; +import { Form, Select, Input, Button } from 'antd'; +import { MinusCircleOutlined, PlusOutlined, InfoCircleOutlined } from '@ant-design/icons'; +import InfoDrawer from '../../common/InfoDrawer'; + +const notificationOptions = [ + { label: 'E-mail', value: 'eMail' }, + { label: 'MS Teams', value: 'msTeams' }, +]; + +const NotificationTab = ({ setNotificationDetails, notificationDetails }) => { + // useEffect(() => {}, [selectedFileMonitoringDetails]); + const [open, setOpen] = useState(false); + + const showDrawer = () => { + setOpen(true); + }; + + const onClose = () => { + setOpen(false); + }; + + return ( + <> + + + + + + + + +

+ Notification Channel + showDrawer()} /> +

+ + + } + name="notificationChannels" + rules={[{ required: true, message: 'Required Field' }]}> + +
+ + {notificationDetails?.notificationChannel?.includes('eMail') ? ( + + {(fields, { add, remove }) => ( + <> + {fields.map((field, _index) => ( + +
+ + + + {fields.length > 1 ? ( + remove(field.name)} + style={{ marginLeft: '10px' }} + /> + ) : null} +
+
+ ))} + + + + + )} +
+ ) : null} + + {notificationDetails?.notificationChannel?.includes('msTeams') ? ( + + {(fields, { add, remove }) => ( + <> + {fields.map((field, _index) => ( + +
+ + + + {fields.length > 1 ? ( + remove(field.name)} + style={{ marginLeft: '10px' }} + /> + ) : null} +
+
+ ))} + + + + + )} +
+ ) : null} + + ); +}; + +export default NotificationTab; diff --git a/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoring.jsx b/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoring.jsx new file mode 100644 index 000000000..01f15d053 --- /dev/null +++ b/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoring.jsx @@ -0,0 +1,120 @@ +import React, { useState, useEffect } from 'react'; +import BreadCrumbs from '../../common/BreadCrumbs'; +import { Tooltip, Button, message } from 'antd'; +import Text from '../../common/Text'; +import OrbitMonitoringTable from './OrbitMonitoringTable'; +import OrbitMonitoringModal from './OrbitMonitoringModal'; +import { authHeader, handleError } from '../../common/AuthHeader.js'; +import { useSelector } from 'react-redux'; + +const OrbitMonitoring = () => { + const [modalVisible, setModalVisible] = useState(false); + const [confirmLoading, setConfirmLoading] = useState(false); + const [orbitBuildList, setOrbitBuildList] = useState(null); + const [selectedOrbitBuild, setSelectedOrbitBuild] = useState(null); + const [editing, setEditing] = useState(null); + + const { + application: { applicationId }, + } = useSelector((state) => state.applicationReducer); + + const addOrbitMonitoring = async () => { + await setSelectedOrbitBuild(null); + await setEditing(null); + await setConfirmLoading(true); + await setModalVisible(true); + await setConfirmLoading(false); + }; + + useEffect(() => { + async function fetchBuilds() { + if (applicationId) { + await getOrbitMonitoring(applicationId); + } + } + fetchBuilds(); + }, [applicationId]); + + //Get list of all orbit monitoring + const getOrbitMonitoring = async (applicationId) => { + try { + const payload = { + method: 'GET', + header: authHeader(), + }; + + const response = await fetch(`/api/orbit/allMonitorings/${applicationId}`, payload); + if (!response.ok) handleError(response); + + const data = await response.json(); + + setOrbitBuildList(data); + } catch (err) { + console.log(err); + } + }; + + const saveOrbitBuildDetails = async (monitoringDetails) => { + try { + const payload = { + method: editing ? 'PUT' : 'POST', + header: authHeader(), + body: JSON.stringify({ + ...monitoringDetails, + id: editing ? editing : selectedOrbitBuild, + build: selectedOrbitBuild, + }), + }; + + console.log(monitoringDetails); + + const response = await fetch('/api/orbit/', payload); + + if (!response.ok) { + handleError(response); + } else { + message.success('Successfully saved orbit build monitoring data'); + getOrbitMonitoring(); + } + } catch (error) { + console.log(error); + message.error('Failed to save orbit build monitoring'); + } + }; + + return ( + <> + + + + } + /> + + + + ); +}; + +export default OrbitMonitoring; diff --git a/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoringModal.jsx b/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoringModal.jsx new file mode 100644 index 000000000..5b6c6dbe0 --- /dev/null +++ b/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoringModal.jsx @@ -0,0 +1,351 @@ +import React, { useState, useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import { Modal, Button, Tabs, Form, message, Spin } from 'antd'; +import BasicTab from './BasicTab'; +import MonitoringTab from './MonitoringTab'; +import NotificationsTab from './NotificationsTab'; +import useWindowSize from '../../../hooks/useWindowSize'; +import { authHeader, handleError } from '../../common/AuthHeader.js'; + +const { TabPane } = Tabs; + +const OrbitMonitoringModal = ({ + modalVisible, + orbitBuildList, + setModalVisible, + saveOrbitBuildDetails, + selectedOrbitBuild, + setSelectedOrbitBuild, + editing, + setEditing, + getOrbitMonitoring, +}) => { + //modal states + const [modalWidth, setModalWidth] = useState(0); + const [activeTab, setActiveTab] = useState('1'); + const [cron, setCron] = useState(null); + const windowSize = useWindowSize(); + const [orbitBuildDetails, setOrbitBuildDetails] = useState(null); + const [monitoringDetails, setMonitoringDetails] = useState({ + isActive: true, + monitoringConditions: [], + }); + const [selectedOrbitBuildDetails, setSelectedOrbitBuildDetails] = useState(null); + const [notificationDetails, setNotificationDetails] = useState({}); + const [entryForm] = Form.useForm(); + const [confirmLoading, setConfirmLoading] = useState(false); + const [fetchingOrbitDetails, setFetchingOrbitDetails] = useState(false); + + const { + application: { applicationId }, + } = useSelector((state) => state.applicationReducer); + + //set fields of form if monitoring is selected + useEffect(() => { + if (!editing) { + return; + } + if (!selectedOrbitBuildDetails) { + getOrbitBuildDetails(selectedOrbitBuild); + } + + //if details have been gotten, set field values + if (selectedOrbitBuildDetails) { + //grab and restructure notification channels to set into fields + let notificationChannels = []; + let emails; + let msTeams; + + if (selectedOrbitBuildDetails.metaData && selectedOrbitBuildDetails.metaData.notifications) { + for (let i = 0; i < selectedOrbitBuildDetails.metaData.notifications.length; i++) { + if (selectedOrbitBuildDetails.metaData.notifications[i].channel === 'eMail') { + emails = selectedOrbitBuildDetails.metaData.notifications[i].recipients; + notificationChannels.push('eMail'); + } + + if (selectedOrbitBuildDetails.metaData.notifications[i].channel === 'msTeams') { + msTeams = selectedOrbitBuildDetails.metaData.notifications[i].recipients; + notificationChannels.push('msTeams'); + } + } + } + + if (selectedOrbitBuildDetails.metaData) { + const { + name, + cron, + isActive, + build, + severityCode, + product, + businessUnit, + host, + primaryContact, + secondaryContact, + metaData: { + monitoringCondition: { notifyCondition, updateInterval, updateIntervalDays, buildStatus, deleted }, + }, + } = selectedOrbitBuildDetails; + + entryForm.setFieldsValue({ + name: name, + monitorName: name, + cron: cron, + build: build, + isActive: isActive, + notificationChannels: notificationChannels, + severityCode: severityCode, + notifyCondition: notifyCondition, + businessUnit: businessUnit, + product: product, + host: host, + primaryContact: primaryContact, + secondaryContact: secondaryContact, + updateInterval: updateInterval, + updateIntervalDays: updateIntervalDays, + buildStatus: buildStatus, + deleted: deleted, + emails: emails, + msTeamsGroups: msTeams, + }); + + setSelectedOrbitBuild(build); + + //set states to have fields appear that are necessary + setCron(cron); + setMonitoringDetails({ + ...monitoringDetails, + monitoringConditions: notifyCondition, + }); + setNotificationDetails({ ...notificationDetails, notificationChannel: notificationChannels }); + } + } + }, [selectedOrbitBuildDetails, selectedOrbitBuild]); + + //validate forms before saving + const validateForms = async () => { + let validationError = null; + let formData = {}; + + try { + formData = await entryForm.validateFields(); + } catch (err) { + validationError = err; + } + + return { validationError, formData }; + }; + + //save data + const handleSave = async () => { + try { + setConfirmLoading(true); + //validate data and throw new error + + const data = await validateForms(); + + if (data.validationError?.errorFields) { + throw new Error('Validation failed, please check form fields again.'); + } + + const formData = data.formData; + + let notificationChannels = formData.notificationChannels; + let emails = formData.emails; + let msTeamsGroups = formData.msTeamsGroups; + + formData.application_id = applicationId; + + // current UTC Time Stamp + const date = new Date(); + const currentTimeStamp = date.getTime(); + + let notifyCondition = formData.notifyCondition; + let updateInterval = formData.updateInterval; + let updateIntervalDays = formData.updateIntervalDays; + let buildStatus = formData.buildStatus; + let deleted = formData.deleted; + + //organize notifications + let notifications = []; + for (let i = 0; i < notificationChannels.length; i++) { + if (notificationChannels[i] == 'eMail') { + notifications.push({ channel: 'eMail', recipients: emails }); + } + if (notificationChannels[i] == 'msTeams') { + notifications.push({ channel: 'msTeams', recipients: msTeamsGroups }); + } + } + + formData.metaData = { + lastMonitored: currentTimeStamp, + monitoringCondition: { + notifyCondition, + updateInterval, + updateIntervalDays, + buildStatus, + deleted, + }, + notifications, + isActive: formData.isActive, + }; + + console.log(formData); + + await saveOrbitBuildDetails(formData); + cancelModal(); + } catch (err) { + setConfirmLoading(false); + console.log(err.message); + if (err.message !== 'Validation failed') message.error(err.message); + } + }; + + const cancelModal = async () => { + await setEditing(null); + await setOrbitBuildDetails(null); + await setSelectedOrbitBuild(null); + await setSelectedOrbitBuildDetails(null); + entryForm.resetFields(); + + await setModalVisible(false); + await setActiveTab('1'); + await setConfirmLoading(false); + await setMonitoringDetails({ + isActive: true, + monitoringConditions: [], + }); + await getOrbitMonitoring(applicationId); + }; + + // Changes modal size per screen vw + useEffect(() => { + const { width } = windowSize.inner; + if (width > 1500) { + setModalWidth('40vw'); + } else if (width > 1000) { + setModalWidth('60vw'); + } else { + setModalWidth('100vw'); + } + }, [windowSize]); + + //Modal footer btns ------------------------------------------------------------------------- + const nextBtn = ( + + ); + + // Get details of a orbit monitoring ----------------------------------------------- + const getOrbitBuildDetails = async (id) => { + try { + const payload = { method: 'GET', header: authHeader() }; + setFetchingOrbitDetails(true); + const response = await fetch(`/api/orbit/getOne/${applicationId}/${id}`, payload); + if (!response.ok) handleError(response); + const data = await response.json(); + + setActiveTab('2'); + setOrbitBuildDetails(data); + + //if selected monitoring, store selected orbit monitoring in different state for later + if (selectedOrbitBuild) { + await setSelectedOrbitBuildDetails(data); + } + } catch (err) { + setFetchingOrbitDetails(false); + } finally { + setFetchingOrbitDetails(false); + } + }; + + const backBtn = ( + + ); + + const saveBtn = ( + + ); + const btns = { + 0: null, + 1: [nextBtn], + 2: [backBtn, nextBtn], + 3: [backBtn, saveBtn], + }; + + return ( + + {fetchingOrbitDetails ? ( +
+ +
+ ) : ( + + { + setActiveTab(record); + }}> + + + + + + + + + + + + )} +
+ ); +}; + +export default OrbitMonitoringModal; diff --git a/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoringTable.jsx b/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoringTable.jsx new file mode 100644 index 000000000..a16984475 --- /dev/null +++ b/client-reactjs/src/components/application/orbitMonitoring/OrbitMonitoringTable.jsx @@ -0,0 +1,120 @@ +import React from 'react'; +import { Table, Space, Tooltip, Badge, message } from 'antd'; +import { DeleteOutlined, EyeOutlined, PauseCircleOutlined, PlayCircleOutlined } from '@ant-design/icons'; +import { authHeader, handleError } from '../../common/AuthHeader.js'; + +const OrbitMonitoringTable = ({ + orbitBuildList, + setOrbitBuildList, + setSelectedOrbitBuild, + setModalVisible, + setEditing, +}) => { + // Delete record + const deleteOrbitBuildMonitoring = async ({ id, name }) => { + try { + const payload = { + method: 'DELETE', + header: authHeader(), + }; + + const response = await fetch(`/api/orbit/delete/${id}/${name}`, payload); + console.log(response); + if (!response.ok) return handleError(response); + + const newOrbitBuildMonitoringList = orbitBuildList.filter((OrbitBuild) => OrbitBuild.id != id); + setOrbitBuildList(newOrbitBuildMonitoringList); + } catch (err) { + message.error('Failed to delete Orbit Build monitoring ', err.message); + } + }; + + // View existing orbit monitoring ------------------------------------------------------------------ + const viewExistingOrbitBuildMonitoring = (id) => { + setEditing(id); + setModalVisible(true); + setSelectedOrbitBuild(id); + }; + + const changeOrbitBuildMonitoringStatus = async (id) => { + try { + const payload = { + method: 'PUT', + header: authHeader(), + }; + + const response = await fetch(`/api/orbit/toggleStatus/${id}`, payload); + if (!response.ok) return handleError(response); + const updatedMonitoringList = orbitBuildList.map((record) => + record.id === id ? { ...record, isActive: !record.isActive } : record + ); + setOrbitBuildList(updatedMonitoringList); + } catch (err) { + message.error('Failed to update file monitoring status'); + } + }; + + const columns2 = [ + { + title: 'Status', + render: (_, record) => ( + <> + + + ), + }, + { title: 'Name', dataIndex: 'name' }, + { title: 'Build', dataIndex: 'build' }, + + { title: 'Cron', dataIndex: 'cron' }, + { title: 'Severity Code', dataIndex: 'severityCode' }, + { + title: 'Delete', + dataIndex: 'delete', + render: (_, record) => ( + + + + viewExistingOrbitBuildMonitoring(record.id)} /> + + + {record.isActive ? ( + + + changeOrbitBuildMonitoringStatus(record.id)} /> + + + ) : ( + + + changeOrbitBuildMonitoringStatus(record.id)} /> + + + )} + + + { + deleteOrbitBuildMonitoring({ id: record.id, name: record.name }); + }} + /> + + + + ), + }, + ]; + return ( + <> +
record.id} + /> + + ); +}; + +export default OrbitMonitoringTable; diff --git a/client-reactjs/src/components/common/Constants.js b/client-reactjs/src/components/common/Constants.js index b16c5ee9d..a05556850 100644 --- a/client-reactjs/src/components/common/Constants.js +++ b/client-reactjs/src/components/common/Constants.js @@ -40,6 +40,8 @@ export const Constants = { LICENSES_RETRIEVED: 'LICENSES_RETRIEVED', CONSTRAINTS_RETRIEVED: 'CONSTRAINTS_RETRIEVED', UPDATE_CONSTRAINTS: 'UPDATE_CONSTRAINTS', + UPDATE_INTEGRATIONS: 'UPDATE_INTEGRATIONS', + INTEGRATIONS_RETRIEVED: 'INTEGRATIONS_RETRIEVED', PROPAGATIONS_CHANGES_INITIATE: 'PROPAGATIONS_CHANGES_INITIATE', PROPAGATIONS_CHANGES_SUCCESS: 'PROPAGATIONS_CHANGES_SUCCESS', diff --git a/client-reactjs/src/components/layout/Header.js b/client-reactjs/src/components/layout/Header.js index e62cd4576..0b237208f 100644 --- a/client-reactjs/src/components/layout/Header.js +++ b/client-reactjs/src/components/layout/Header.js @@ -143,6 +143,7 @@ class AppHeader extends Component { this.props.dispatch(applicationActions.getConsumers()); this.props.dispatch(applicationActions.getLicenses()); this.props.dispatch(applicationActions.getConstraints()); + this.props.dispatch(applicationActions.getIntegrations(this.props.application.applicationId)); } if (this.props.newApplication) { diff --git a/client-reactjs/src/components/layout/LeftNav.js b/client-reactjs/src/components/layout/LeftNav.js index cfb4e6a36..9ed30fe14 100644 --- a/client-reactjs/src/components/layout/LeftNav.js +++ b/client-reactjs/src/components/layout/LeftNav.js @@ -11,6 +11,8 @@ import { ClockCircleOutlined, ContainerOutlined, BarChartOutlined, + CloudServerOutlined, + ApiOutlined, BellOutlined, } from '@ant-design/icons'; @@ -59,6 +61,9 @@ class LeftNav extends Component { render() { const applicationId = this.props?.applicationId || ''; + const integrations = this.props?.integrations || []; + + const orbitActive = integrations.find((i) => i.name === 'Orbit')?.active; if (!this.props.loggedIn || !this.props.user || Object.getOwnPropertyNames(this.props.user).length == 0) { return null; @@ -118,6 +123,12 @@ class LeftNav extends Component { }> {} + + {orbitActive ? ( + }> + {} + + ) : null} } key="5"> @@ -127,6 +138,12 @@ class LeftNav extends Component { }> {} + + {orbitActive ? ( + }> + {} + + ) : null} {canEdit ? ( @@ -163,8 +180,11 @@ class LeftNav extends Component { }> {} + }> + {} + : }> Compliance @@ -179,10 +199,11 @@ class LeftNav extends Component { } function mapStateToProps(state) { + const integrations = state.applicationReducer.integrations; const applicationId = state.applicationReducer.application?.applicationId; const { loggedIn, user } = state.authenticationReducer; const isReportLoading = state.propagation.changes.loading || state.propagation.current.loading; - return { applicationId, loggedIn, user, isReportLoading }; + return { applicationId, integrations, loggedIn, user, isReportLoading }; } let connectedLeftNav = connect(mapStateToProps, null, null, { forwardRef: true })(withRouter(LeftNav)); diff --git a/client-reactjs/src/index.css b/client-reactjs/src/index.css index 7c5860c79..89b7cc661 100644 --- a/client-reactjs/src/index.css +++ b/client-reactjs/src/index.css @@ -1073,3 +1073,37 @@ div.tooltip { .sentNotificationBody p:last-child { display: none; } + +.OrbitTable .ant-table-thead > tr > th { + background: #002140; + color: white; +} + +.OrbitTable .ant-table-thead > tr > .ant-table-column-sort:hover { + color: black; +} + +.OrbitTable .ant-table-cell-scrollbar { + box-shadow: none; +} + +.OrbitTable tr:nth-child(2n) { + background: lightgrey; +} + +.OrbitTable tr:nth-child(2n) { + background: lightgrey; +} + +.OrbitTable tr:nth-child(2n) { + background: lightgrey; +} + +.OrbitTable .ant-table-row-level-0:nth-child(2n) .ant-table-column-sort { + background: lightgrey; +} + +.chartLabel { + color: rgb(71, 70, 70); + margin-bottom: 2rem; +} diff --git a/client-reactjs/src/redux/actions/Application.js b/client-reactjs/src/redux/actions/Application.js index b4d59ec5e..eaccbfd9b 100644 --- a/client-reactjs/src/redux/actions/Application.js +++ b/client-reactjs/src/redux/actions/Application.js @@ -11,6 +11,8 @@ export const applicationActions = { getLicenses, getConstraints, updateConstraints, + getIntegrations, + updateIntegrations, }; function applicationSelected(applicationId, applicationTitle) { @@ -80,3 +82,16 @@ function getConstraints() { function updateConstraints(constraints) { return { type: Constants.UPDATE_CONSTRAINTS, constraints }; } + +function getIntegrations(applicationId) { + return (dispatch) => { + fetch(`/api/integrations/get/${applicationId}`, { headers: authHeader() }) + .then((response) => (response.ok ? response.json() : handleError(response))) + .then((integrations) => dispatch({ type: Constants.INTEGRATIONS_RETRIEVED, integrations })) + .catch(console.log); + }; +} + +function updateIntegrations(integrations) { + return { type: Constants.UPDATE_INTEGRATIONS, integrations }; +} diff --git a/client-reactjs/src/redux/reducers/ApplicationReducer.js b/client-reactjs/src/redux/reducers/ApplicationReducer.js index f131228bb..fc67121e9 100644 --- a/client-reactjs/src/redux/reducers/ApplicationReducer.js +++ b/client-reactjs/src/redux/reducers/ApplicationReducer.js @@ -9,6 +9,7 @@ const initialState = { consumers: [], licenses: [], constraints: [], + integrations: [], }; export function applicationReducer(state = initialState, action) { @@ -67,6 +68,16 @@ export function applicationReducer(state = initialState, action) { ...state, constraints: action.constraints, }; + case Constants.INTEGRATIONS_RETRIEVED: + return { + ...state, + integrations: action.integrations, + }; + case Constants.UPDATE_INTEGRATIONS: + return { + ...state, + integrations: action.integrations, + }; default: return state; } diff --git a/eclcc.log b/eclcc.log new file mode 100644 index 000000000..3a13810da --- /dev/null +++ b/eclcc.log @@ -0,0 +1,13 @@ +00000000 PRG 2024-01-30 08:05:30 21740 4624 "Error loading C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\archive.dll: 126 - The specified module could not be found." +00000001 PRG 2024-01-30 08:05:30 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\archive.dll could not be loaded" +00000002 PRG 2024-01-30 08:05:30 21740 4624 "Error loading C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\archivefile.dll: 126 - The specified module could not be found." +00000003 PRG 2024-01-30 08:05:30 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\archivefile.dll could not be loaded" +00000004 PRG 2024-01-30 08:05:31 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\bz2.dll could not be loaded" +00000005 PRG 2024-01-30 08:05:31 21740 4624 "Error loading C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\git2.dll: 126 - The specified module could not be found." +00000006 PRG 2024-01-30 08:05:31 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\git2.dll could not be loaded" +00000007 PRG 2024-01-30 08:05:31 21740 4624 "Error loading C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\gitfile.dll: 126 - The specified module could not be found." +00000008 PRG 2024-01-30 08:05:31 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\gitfile.dll could not be loaded" +00000009 PRG 2024-01-30 08:05:31 21740 4624 "Error loading C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\gitfile.lib: 193 - 193" +0000000A PRG 2024-01-30 08:05:31 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\gitfile.lib could not be loaded" +0000000B PRG 2024-01-30 08:05:31 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\pcre.dll could not be loaded" +0000000C PRG 2024-01-30 08:05:31 21740 4624 "File hook library C:\Program Files\HPCCSystems\8.10.12\clienttools\filehooks\zlib1.dll could not be loaded" diff --git a/server/.gitignore b/server/.gitignore index 34d11596b..c095a2709 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -33,3 +33,8 @@ cluster-whitelist.js eclcc.log logs/* +#timezone files +/eclDir + +#temp files +/tempFiles \ No newline at end of file diff --git a/server/job-scheduler.js b/server/job-scheduler.js index a4ff31078..cdaaf3082 100644 --- a/server/job-scheduler.js +++ b/server/job-scheduler.js @@ -42,6 +42,17 @@ const { const { scheduleKeyCheck } = require("./jobSchedularMethods/apiKeys.js"); const {scheduleEmailNotificationProcessing, scheduleTeamsNotificationProcessing} = require("./jobSchedularMethods/notificationJobs.js"); +const { + createOrbitMegaphoneJob, + createOrbitMonitoringJob, + scheduleOrbitMonitoringOnServerStart, +} = require("./jobSchedularMethods/orbitJobs.js"); + +const { scheduleKeyCheck } = require("./jobSchedularMethods/apiKeys.js"); + +//import job directly to run it only once on server start +const { createIntegrations } = require("./jobs/integrationCreation.js"); + class JobScheduler { constructor() { this.bree = new Bree({ @@ -112,6 +123,11 @@ class JobScheduler { await this.createClusterUsageHistoryJob(); await this.scheduleEmailNotificationProcessing(); await this.scheduleTeamsNotificationProcessing(); + await this.scheduleOrbitMonitoringOnServerStart(); + await this.createOrbitMegaphoneJob(); + + //one off jobs on server start + await this.createIntegrations(); })(); } @@ -316,6 +332,21 @@ class JobScheduler { scheduleTeamsNotificationProcessing(){ return scheduleTeamsNotificationProcessing.call(this); } + //orbit jobs + createOrbitMegaphoneJob() { + return createOrbitMegaphoneJob.call(this); + } + scheduleOrbitMonitoringOnServerStart() { + return scheduleOrbitMonitoringOnServerStart.call(this); + } + + createOrbitMonitoringJob({ orbitMonitoring_id, cron }) { + return createOrbitMonitoringJob.call(this, { orbitMonitoring_id, cron }); + } + + createIntegrations() { + return createIntegrations.call(this); + } } module.exports = new JobScheduler(); diff --git a/server/jobSchedularMethods/clusterJobs.js b/server/jobSchedularMethods/clusterJobs.js index ac1fdb4e3..1b06513eb 100644 --- a/server/jobSchedularMethods/clusterJobs.js +++ b/server/jobSchedularMethods/clusterJobs.js @@ -47,7 +47,7 @@ function createClusterMonitoringBreeJob({ clusterMonitoring_id, cron }) { const job = { cron, name: uniqueJobName, - path: path.join(__dirname, "jobs", SUBMIT_CLUSTER_MONITORING_JOB), + path: path.join(__dirname, "..", "jobs", SUBMIT_CLUSTER_MONITORING_JOB), worker: { workerData: { clusterMonitoring_id }, }, diff --git a/server/jobSchedularMethods/hpccFiles.js b/server/jobSchedularMethods/hpccFiles.js index 5a9af6d65..7bfa0bb3e 100644 --- a/server/jobSchedularMethods/hpccFiles.js +++ b/server/jobSchedularMethods/hpccFiles.js @@ -142,50 +142,6 @@ async function scheduleFileMonitoringOnServerStart() { } } -async function scheduleFileMonitoringOnServerStart() { - try { - const activeLandingZoneFileMonitoring = await FileMonitoring.findAll({ - where: { - monitoringActive: true, - // monitoringAssetType: "landingZoneFile", - }, - raw: true, - }); - for (const monitoring of activeLandingZoneFileMonitoring) { - await this.scheduleFileMonitoringBreeJob({ - filemonitoring_id: monitoring.id, - name: monitoring.name, - cron: monitoring.cron, - monitoringAssetType: monitoring.monitoringAssetType, - }); - } - } catch (err) { - logger.error(err); - } -} - -async function scheduleFileMonitoringOnServerStart() { - try { - const activeLandingZoneFileMonitoring = await FileMonitoring.findAll({ - where: { - monitoringActive: true, - // monitoringAssetType: "landingZoneFile", - }, - raw: true, - }); - for (const monitoring of activeLandingZoneFileMonitoring) { - await this.scheduleFileMonitoringBreeJob({ - filemonitoring_id: monitoring.id, - name: monitoring.name, - cron: monitoring.cron, - monitoringAssetType: monitoring.monitoringAssetType, - }); - } - } catch (err) { - logger.error(err); - } -} - async function scheduleFileMonitoring() { logger.info("📂 FILE MONITORING STARTED ..."); try { diff --git a/server/jobSchedularMethods/orbitJobs.js b/server/jobSchedularMethods/orbitJobs.js new file mode 100644 index 000000000..be57b40b2 --- /dev/null +++ b/server/jobSchedularMethods/orbitJobs.js @@ -0,0 +1,58 @@ +const path = require("path"); +const logger = require("../config/logger"); + +const models = require("../models"); +orbitMonitoring = models.orbitMonitoring; + +const MEGAPHONE_JOB = "orbitMegaphone.js"; +const ORBIT_MONITORING = "submitOrbitMonitoring.js"; + +function createOrbitMegaphoneJob() { + const uniqueJobName = `Orbit Megaphone Job`; + const job = { + interval: "30m", + name: uniqueJobName, + path: path.join(__dirname, "..", "jobs", MEGAPHONE_JOB), + }; + this.bree.add(job); + this.bree.start(uniqueJobName); + logger.info("📈 ORBIT MEGAPHONE JOB STARTED ..."); +} + +function createOrbitMonitoringJob({ orbitMonitoring_id, cron }) { + const uniqueJobName = `Orbit Monitoring - ${orbitMonitoring_id}`; + const job = { + cron, + name: uniqueJobName, + path: path.join(__dirname, "..", "jobs", ORBIT_MONITORING), + worker: { + workerData: { orbitMonitoring_id }, + }, + }; + this.bree.add(job); + this.bree.start(uniqueJobName); +} + +async function scheduleOrbitMonitoringOnServerStart() { + try { + logger.info("📺 ORBIT MONITORING STARTED ..."); + const orbitMonitorings = await orbitMonitoring.findAll({ raw: true }); + for (let monitoring of orbitMonitorings) { + const { id, cron, isActive } = monitoring; + if (isActive) { + this.createOrbitMonitoringJob({ + orbitMonitoring_id: id, + cron, + }); + } + } + } catch (err) { + logger.error(err); + } +} + +module.exports = { + createOrbitMegaphoneJob, + createOrbitMonitoringJob, + scheduleOrbitMonitoringOnServerStart, +}; diff --git a/server/jobs/integrationCreation.js b/server/jobs/integrationCreation.js new file mode 100644 index 000000000..eebb00ee4 --- /dev/null +++ b/server/jobs/integrationCreation.js @@ -0,0 +1,60 @@ +const { parentPort } = require("worker_threads"); + +const logger = require("../config/logger"); +const models = require("../models"); +const application = models.application; +const integrations = models.integrations; + +async function createIntegrations() { + try { + //grab all applications so we can have one entry per integration per application + const applications = await application.findAll({}); + + let integrationList = []; + + //build list of integrations + applications.map((application) => { + //for each application, add an object of each integration, for now we only have orbit + + if (process.env.ASR === "true") { + integrationList.push({ + application_id: application.id, + name: "Orbit", + description: + "Enabling this integration will allow Tombolo to collect data from HPCCs Orbit system and provide dashboard information for it", + active: "false", + metaData: { + notificationEmails: "matthew.fancher@lexisnexisrisk.com", + notificationWebhooks: + "https://reedelsevier.webhook.office.com/webhookb2/81c072d6-6b47-4eca-9434-73944c464876@9274ee3f-9425-4109-a27f-9fb15c10675d/IncomingWebhook/60019c7653734064b4c225a02b1da597/af40e12f-e839-4801-91e9-e61a20045feb", + }, + }); + } + }); + + await Promise.all( + //create integrations, double checking they don't already exist + integrationList.map(async (integration) => { + let exists = await integrations.findOne({ + where: { + name: integration.name, + application_id: integration.application_id, + }, + raw: true, + }); + + //if it doesn't exist, create integration + if (!exists) { + await integrations.create(integration); + } + return true; + }) + ); + } catch (error) { + logger.error("Failed to create integrations, error: " + error); + } finally { + if (parentPort) parentPort.postMessage("done"); + } +} + +module.exports = { createIntegrations }; diff --git a/server/jobs/messageCards/notificationTemplate.js b/server/jobs/messageCards/notificationTemplate.js index b200bd89d..b9fa6951b 100644 --- a/server/jobs/messageCards/notificationTemplate.js +++ b/server/jobs/messageCards/notificationTemplate.js @@ -28,7 +28,7 @@ module.exports = { body += `
${keys}: ${details[keys]}
`; } - if (tableRows !== "") { + if (tableRows !== "2") { body = body + table; } @@ -36,6 +36,314 @@ module.exports = { return body; }, + //orbit monitoring email body + orbitMonitoringEmailBody: function (buildDetails) { + //build out issue row + let issue = ``; + + if ( + buildDetails.issue.metaDifference && + buildDetails.issue.metaDifference.length > 0 + ) { + buildDetails.issue.metaDifference.forEach((meta) => { + issue += `Issue: ${meta.attribute}
`; + issue += `Detected Value: ${meta.newValue}
`; + }); + } + + issue += `HOST: ${buildDetails.issue.host}
`; + issue += `JOB NAME: ${buildDetails.issue.build}
`; + issue += `MONITORING SCHEDULE: ${buildDetails.issue.cron}
`; + issue += `RETURNED JOB DATE: ${buildDetails.date}
`; + issue += `RETURNED JOB STATE: ${buildDetails.issue.status}
`; + issue += `RETURNED JOB WUID: ${buildDetails.issue.workUnit}
`; + + let tableRows = `
+ + + + + + + `; + + const table = `
PRODUCT${buildDetails.product.toUpperCase()}
ISSUE${issue}
SEV_CODE${ + buildDetails.severityCode + }
DATE_DETECTED${ + buildDetails.date + }
REMEDY${ + buildDetails.remedy + }
REGION${ + buildDetails.region + }
BUSINESS_UNIT${ + buildDetails.businessUnit + }
NOTIFICATION_ID${ + buildDetails.notification_id + }
+ + ${tableRows} +
`; + + let body = + "

Tombolo has detected an Orbit Build Monitoring Condition.



"; + + body = body + table; + + body = body + `

-Tombolo

`; + return body; + }, + + // TODO make this message card general by changing the key name + orbitMonitoringMessageCard: function (title, buildDetails, notification_id) { + let issue = ``; + + if ( + buildDetails.issue.metaDifference && + buildDetails.issue.metaDifference.length > 0 + ) { + buildDetails.issue.metaDifference.forEach((meta) => { + issue += `Issue: ${meta.attribute}
`; + issue += `Detected Value: ${meta.newValue}
`; + }); + } + + issue += `HOST: ${buildDetails.issue.host}
`; + issue += `JOB NAME: ${buildDetails.issue.build}
`; + issue += `MONITORING SCHEDULE: ${buildDetails.issue.cron}
`; + issue += `RETURNED JOB DATE: ${buildDetails.date}
`; + issue += `RETURNED JOB STATE: ${buildDetails.issue.status}
`; + issue += `RETURNED JOB WUID: ${buildDetails.issue.workUnit}
`; + + let tableRows = `PRODUCT${buildDetails.product.toUpperCase()} + ISSUE${issue} + SEV_CODE${ + buildDetails.severityCode + } + DATE_DETECTED${ + buildDetails.date + } + REMEDY${ + buildDetails.remedy + } + REGION${ + buildDetails.region + } + BUSINESS_UNIT${ + buildDetails.businessUnit + } + NOTIFICATION_ID${ + buildDetails.notification_id + }`; + + const table = `
+ + ${tableRows} +
`; + + let allFacts = []; + + allFacts.push({ name: "", value: table }); + cardData = `"notification_id": "${notification_id}"`; + + const body = JSON.stringify({ + "@type": "MessageCard", + "@context": "https://schema.org/extensions", + summary: title, + themeColor: "0072C6", + title: title, + sections: [ + { + facts: allFacts, + }, + { + type: "MessageCard", + contentType: "text/html", + text: " ", + }, + ], + + potentialAction: [ + { + "@type": "ActionCard", + name: "Add a comment", + inputs: [ + { + "@type": "TextInput", + id: "comment", + isMultiline: false, + title: "Add a comment here for this task", + }, + ], + actions: [ + { + "@type": "HttpPOST", + name: "Add comment", + target: process.env.API_URL + "/api/updateNotification/update", + body: `{"comment":"{{comment.value}}", ${cardData}}`, + isRequired: true, + errorMessage: "Comment cannot be blank", + }, + ], + }, + { + "@type": "ActionCard", + name: "Change status", + inputs: [ + { + "@type": "MultichoiceInput", + id: "list", + title: "Select a status", + isMultiSelect: "false", + choices: [ + { + display: "Triage", + value: "triage", + }, + { + display: "In Progress", + value: "inProgress", + }, + { + display: "Completed", + value: "completed", + }, + ], + }, + ], + actions: [ + { + "@type": "HttpPOST", + name: "Save", + target: process.env.API_URL + "/api/updateNotification/update", + body: `{"status":"{{list.value}}", ${cardData}}`, + isRequired: true, + errorMessage: "Select an option", + }, + ], + }, + ], + }); + + return body; + }, + //Orbit Build email body + + orbitBuildEmailBody: function (buildDetails) { + const tableRows = ` + ${buildDetails.name} + ${buildDetails.status} + ${buildDetails.subStatus} + ${buildDetails.lastrun} + ${buildDetails.HpccWorkUnit} + ${buildDetails.lastRun} + ${buildDetails.workunit} + `; + + const table = `
+ + ${tableRows} +
Name Status SubStatusLast Run Work Unit
`; + + let body = + "

Tombolo has detected an Orbit Build with a megaphone substatus.



"; + body = body + table; + body = body + `

-Tombolo

`; + + return body; + }, + + // TODO make this message card general by changing the key name + orbitBuildMessageCard: function (title, facts, notification_id) { + let allFacts = []; + cardData = `"notification_id": "${notification_id}"`; + facts.forEach((fact) => { + for (let key in fact) { + allFacts.push({ name: key, value: fact[key] }); + } + allFacts = [...allFacts]; + }); + const body = JSON.stringify({ + "@type": "MessageCard", + "@context": "https://schema.org/extensions", + summary: title, + themeColor: "0072C6", + title: title, + sections: [ + { + facts: allFacts, + }, + { + type: "MessageCard", + contentType: "text/html", + text: " ", + }, + ], + + potentialAction: [ + { + "@type": "ActionCard", + name: "Add a comment", + inputs: [ + { + "@type": "TextInput", + id: "comment", + isMultiline: false, + title: "Add a comment here for this task", + }, + ], + actions: [ + { + "@type": "HttpPOST", + name: "Add comment", + target: process.env.API_URL + "/api/updateNotification/update", + body: `{"comment":"{{comment.value}}", ${cardData}}`, + isRequired: true, + errorMessage: "Comment cannot be blank", + }, + ], + }, + { + "@type": "ActionCard", + name: "Change status", + inputs: [ + { + "@type": "MultichoiceInput", + id: "list", + title: "Select a status", + isMultiSelect: "false", + choices: [ + { + display: "Triage", + value: "triage", + }, + { + display: "In Progress", + value: "inProgress", + }, + { + display: "Completed", + value: "completed", + }, + ], + }, + ], + actions: [ + { + "@type": "HttpPOST", + name: "Save", + target: process.env.API_URL + "/api/updateNotification/update", + body: `{"status":"{{list.value}}", ${cardData}}`, + isRequired: true, + errorMessage: "Select an option", + }, + ], + }, + ], + }); + + return body; + }, + //Cluster monitoring email body clusterMonitoringEmailBody: function (facts) { let body = "
"; @@ -60,7 +368,7 @@ module.exports = { } allFacts = [...allFacts]; }); - const body = JSON.stringify({ + const body = { "@type": "MessageCard", "@context": "https://schema.org/extensions", summary: title, @@ -137,12 +445,11 @@ module.exports = { ], }, ], - }); + }; return body; }, - //Message card for landing zone messageCardBody: function ({ notificationDetails, diff --git a/server/jobs/orbitMegaphone.js b/server/jobs/orbitMegaphone.js new file mode 100644 index 000000000..19547cc4b --- /dev/null +++ b/server/jobs/orbitMegaphone.js @@ -0,0 +1,187 @@ +const logger = require("../config/logger"); +const sql = require("mssql"); +const models = require("../models"); +const integrations = models.integrations; +const orbitBuilds = models.orbitBuilds; +const monitoring_notifications = models.monitoring_notifications; +const notificationTemplate = require("./messageCards/notificationTemplate"); +const { notify } = require("../routes/notifications/email-notification"); +const { v4: uuidv4 } = require("uuid"); +const axios = require("axios"); + +const dbConfig = { + server: process.env.ORBIT_DB, + database: process.env.ORBIT_DB_NAME, + user: process.env.ORBIT_DB_USER, + password: process.env.ORBIT_DB_PWD, + port: parseInt(process.env.ORBIT_DB_PORT), + trustServerCertificate: true, +}; + +(async () => { + try { + //grab all orbit integrations that are active + const orbitIntegrations = await integrations.findAll({ + where: { + name: "Orbit", + active: true, + }, + }); + const sentNotifications = []; + + if (!orbitIntegrations.length) return; + + //if there are active integrations, grab new data and send notifications + orbitIntegrations.map(async (integration) => { + //if megaphone is not active, stop here and don't run + if (!integration.config.megaphoneActive) return; + + let application_id = integration.application_id; + + //connect to db + await sql.connect(dbConfig); + + const result = + await sql.query`select TOP 25 * from DimReceiveInstance where SubStatus_Code = 'MEGAPHONE' order by DateUpdated desc`; + + //just grab the rows from result + let rows = result?.recordset; + //loop through rows to build notifications and import + await Promise.all( + rows.map(async (build) => { + //check if the build already exists + let orbitBuild = await orbitBuilds.findOne({ + where: { + build_id: build.ReceiveInstanceIdKey, + application_id: application_id, + }, + raw: true, + }); + + //if it doesn't exist, create it and send a notification + if (!orbitBuild) { + //create build + const newBuild = await orbitBuilds.create({ + application_id: application_id, + build_id: build.ReceiveInstanceIdKey, + monitoring_id: null, + name: build.FileName, + type: "megaphone", + wuid: build.HpccWorkUnit, + metaData: { + lastRun: build.DateUpdated, + status: build.Status_Code, + subStatus: build.SubStatus_Code, + EnvironmentName: build.EnvironmentName, + initialStatus: build.Status_Code, + finalStatus: build.Status_Code, + }, + }); + + //if megaphone, send notification + // if (build.SubStatus_Code === "MEGAPHONE") + + //build and send email notification + if (integration.metaData.notificationEmails) { + let buildDetails = { + name: newBuild.name, + status: newBuild.metaData.status, + subStatus: newBuild.metaData.subStatus, + lastRun: newBuild.metaData.lastRun, + workunit: newBuild.metaData.workunit, + }; + + const emailBody = + notificationTemplate.orbitBuildEmailBody(buildDetails); + const emailRecipients = integration.metaData.notificationEmails; + + const notificationResponse = await notify({ + to: emailRecipients, + from: process.env.EMAIL_SENDER, + subject: + "Alert: Megaphone Substatus detected on Orbit Build " + + build.Name, + text: emailBody, + html: emailBody, + }); + + let notification_id = uuidv4(); + + sentNotifications.push({ + id: notification_id, + status: "notified", + notifiedTo: emailRecipients, + notification_channel: "eMail", + application_id, + notification_reason: "Megaphone Substatus", + monitoring_id: newBuild.id, + monitoring_type: "megaphone", + }); + } + + // //build and send Teams notification + if (integration.metaData.notificationWebhooks) { + let facts = [ + { name: newBuild.name }, + { Status: newBuild.metaData.status }, + { "Sub Status": newBuild.metaData.subStatus }, + { "Last Run": newBuild.metaData.lastRun }, + { WorkUnit: newBuild.metaData.workunit }, + ]; + let title = "Orbit Build Detectd With Megaphone Status"; + notification_id = uuidv4(); + const cardBody = notificationTemplate.orbitBuildMessageCard( + title, + facts, + notification_id + ); + + await axios.post( + integration.metaData.notificationWebhooks, + JSON.parse(cardBody) + ); + + sentNotifications.push({ + id: notification_id, + status: "notified", + notifiedTo: integration.metaData.notificationWebhooks, + notification_channel: "msTeams", + application_id, + notification_reason: "Megaphone Substatus", + monitoring_id: newBuild.id, + monitoring_type: "orbit", + }); + } + } else { + //if it does exist, update the "final status metadata" + + await orbitBuilds.update( + { + metaData: { + ...orbitBuild.metaData, + finalStatus: build.Status_Code, + }, + }, + { + where: { + build_id: build.ReceiveInstanceIdKey, + application_id: application_id, + }, + } + ); + } + + return true; + }) + ); + + // Record notifications + if (sentNotifications.length > 0) { + monitoring_notifications.bulkCreate(sentNotifications); + } + return; + }); + } catch (error) { + logger.error("Error while running Orbit Jobs: " + error); + } +})(); diff --git a/server/jobs/submitOrbitMonitoring.js b/server/jobs/submitOrbitMonitoring.js new file mode 100644 index 000000000..8aa25e3ce --- /dev/null +++ b/server/jobs/submitOrbitMonitoring.js @@ -0,0 +1,363 @@ +const axios = require("axios"); +const { notify } = require("../routes/notifications/email-notification"); +const { parentPort, workerData } = require("worker_threads"); +const logger = require("../config/logger"); +const models = require("../models"); +const orbitMonitoring = models.orbitMonitoring; +const orbitBuilds = models.orbitBuilds; +const { v4: uuidv4 } = require("uuid"); +const monitoring_notifications = models.monitoring_notifications; +const { + orbitMonitoringEmailBody, + orbitMonitoringMessageCard, +} = require("./messageCards/notificationTemplate"); +const { update } = require("lodash"); + +const sql = require("mssql"); + +const dbConfig = { + server: process.env.ORBIT_DB, + database: process.env.ORBIT_DB_NAME, + user: process.env.ORBIT_DB_USER, + password: process.env.ORBIT_DB_PWD, + port: parseInt(process.env.ORBIT_DB_PORT), + trustServerCertificate: true, +}; + +const runSQLQuery = async (query) => { + try { + await sql.connect(dbConfig); + + const result = await sql.query(query); + + return result; + } catch (err) { + console.log(err); + return { + err, + message: "There was an issue contacting the orbit reports server", + }; + } +}; + +(async () => { + try { + const orbitMonitoringDetails = await orbitMonitoring.findOne({ + where: { id: workerData.orbitMonitoring_id }, + raw: true, + }); + + const { + id, + name, + cron, + build, + isActive, + severityCode, + product, + businessUnit, + host, + primaryContact, + secondaryContact, + application_id, + metaData: { + lastWorkUnit, + notifications, + monitoringCondition, + monitoringCondition: { notifyCondition }, + }, + } = orbitMonitoringDetails; + + //get most recent WorkUnits + + const query = `select Top 10 HpccWorkUnit as 'WorkUnit', Name as 'Build', DateUpdated as 'Date', Status_Code as 'Status', Version, BuildTemplateIdKey as 'BuildID' from DimBuildInstance where Name = '${build}' order by Date desc`; + + const wuResult = await runSQLQuery(query); + + if (wuResult.err) { + throw Error(result.message); + } + + // Keep track of changes + const metaDifference = []; + + if (!wuResult || !wuResult.recordset || wuResult.recordset[0]) return; + + //check for most recent workunit changes and push to metadifference + if (notifyCondition.includes("buildStatus")) { + let newStatus = wuResult.recordset[0].Status; + newStatus = newStatus.toLowerCase(); + + if (monitoringCondition?.buildStatus.includes(newStatus)) { + //build status has been detected, push difference. + metaDifference.push({ + attribute: "Build Status", + oldValue: `${orbitMonitoringDetails.metaData.lastWorkUnit.lastWorkUnitStatus}`, + newValue: `${newStatus}`, + }); + } + } + + if ( + notifyCondition.includes("updateInterval") || + notifyCondition.includes("updateIntervalDays") + ) { + //update interval is in days, so multiply by 86400000 to get number of milliseconds between updates + let updateInterval = monitoringCondition?.updateInterval; + let updateIntervalDays = monitoringCondition?.updateIntervalDays; + + // let orbit = await hpccUtil.getorbit(clusterid, Name); + + // let newModified = orbit.modified; + + //dates to check update interval + const lastDate = new Date(modified); + const newDate = new Date(newModified); + const currentDate = new Date(); + + //get integer difference of days + const diffInMilliseconds = Math.abs(newDate - lastDate); + const diffDays = Math.ceil(diffInMilliseconds / (1000 * 60 * 60 * 24)); + + //get integer difference of days to current date + const diffInMilliCurrent = Math.abs(currentDate - lastDate); + const diffDaysCurrent = Math.ceil( + diffInMilliCurrent / (1000 * 60 * 60 * 24) + ); + + //if difference in days !== update interval, and the Orbit Build has been updated, notify + if (diffDays !== updateInterval && diffDays !== 0) { + metaDifference.push({ + attribute: "Orbit Build did not follow update schedule", + oldValue: `${updateInterval} - days defined between updates`, + newValue: `${diffDays} - days between last updates`, + }); + } + + //if current amount of days is > defined + if (diffDaysCurrent > updateInterval) { + metaDifference.push({ + attribute: "Orbit Build is overdue for update", + oldValue: `${updateInterval} - days defined between updates`, + newValue: `${diffDaysCurrent} - days since last update`, + }); + } + //if updateIntervalDays is set, check that most recent modified day of the week matches setting + if (updateIntervalDays?.length) { + const daysOfWeek = [ + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + ]; + const newDate = new Date(newModified); + const newDayUpdated = daysOfWeek[newDate.getDay()]; + + if (!updateIntervalDays.includes(newDayUpdated)) { + metaDifference.push({ + attribute: + "Orbit Build was updated on a day of the week not defined", + oldValue: `${updateIntervalDays} - days defined`, + newValue: `${newDayUpdated} - day updated`, + }); + } + } + + // update orbit monitoring last monitored date + const date = new Date(); + const currentTimeStamp = date.getTime(); + metaData = orbitMonitoringDetails.metaData; + metaData.lastMonitored = currentTimeStamp; + + await orbitMonitoring.update({ metaData }, { where: { id } }); + } + + //------------------------------------------------------------------------------ + // put result into orbitbuilds table + //check for status changes of the all gathered workunits + + await Promise.all( + wuResult.recordset.map(async (result) => { + //check if result is is orbitbuilds table + const orbitBuild = await orbitBuilds.findOne({ + where: { + wuid: result.WorkUnit, + monitoring_id: id, + application_id: application_id, + }, + raw: true, + }); + + if (orbitBuild) { + //if it does, update it + await orbitBuilds.update( + { + metaData: { + ...orbitBuild.metaData, + finalStatus: result.Status, + }, + }, + { where: { id: orbitBuild.id } } + ); + } else { + console.log("new build found, creating it!!" + result); + //if it doesn't, create it + await orbitBuilds.create({ + application_id: application_id, + build_id: result.BuildID, + monitoring_id: id, + name: result.Build, + type: "orbit", + wuid: result.WorkUnit, + metaData: { + lastRun: result.Date, + status: result.Status, + workunit: result.WorkUnit, + initialStatus: result.Status, + finalStatus: result.Status, + version: result.Version, + }, + }); + } + return true; + }) + ); + + //------------------------------------------------------------------------------ + + // Check what notification channel is set up + let emailNotificationDetails; + let teamsNotificationDetails; + + // notifications.channel === "eMail" + + for (let notification of notifications) { + if (notification.channel === "eMail") { + emailNotificationDetails = notification; + } + if (notification.channel === "msTeams") { + teamsNotificationDetails = notification; + } + } + + const sentNotifications = []; + + let notificationDetails = {}; + + if (metaDifference.length > 0) { + // Note - this does not cover Orbit Build size not in range + notificationDetails.value = + "Orbit Build alert has been triggered by Tombolo"; + notificationDetails.title = `Orbit Build alert has been triggered by Tombolo`; + notificationDetails.text = `Orbit Build alert has been triggered by Tombolo`; + } + + const notification_id = uuidv4(); + + //build out buildDetails for notification + + const buildDetails = { + title: notificationDetails.title, + product: product, + businessUnit: businessUnit, + region: "USA", + severityCode: severityCode, + date: wuResult.recordset[0].Date, + remedy: + "Please Contact one of the following for assistance
" + + "PRIMARY: " + + primaryContact + + "
" + + "SECONDARY: " + + secondaryContact, + notification_id: notification_id, + issue: { + metaDifference: metaDifference, + status: wuResult.recordset[0].Status, + build: build, + host: host, + workUnit: wuResult.recordset[0].WorkUnit, + cron: cron, + }, + }; + + // E-mail notification + if (emailNotificationDetails && notificationDetails.text) { + try { + const body = orbitMonitoringEmailBody(buildDetails); + const notificationResponse = await notify({ + to: emailNotificationDetails.recipients, + from: process.env.EMAIL_SENDER, + subject: notificationDetails.title, + text: body, + html: body, + }); + + if (notificationResponse.accepted) { + sentNotifications.push({ + id: notification_id, + file_name: build, + monitoring_type: "orbitMonitoring", + status: "notified", + notifiedTo: emailNotificationDetails.recipients, + notification_channel: "eMail", + application_id, + notification_reason: notificationDetails.value, + monitoring_id: id, + }); + } + } catch (err) { + logger.error(err); + } + } + + // Teams notification + if (teamsNotificationDetails && notificationDetails.text) { + const { recipients } = teamsNotificationDetails; + + for (let recipient of recipients) { + let title = "Orbit Monitoring alert has been triggered by Tombolo"; + + try { + let body = orbitMonitoringMessageCard( + title, + buildDetails, + notification_id + ); + + await axios.post(recipient, JSON.parse(body)); + + sentNotifications.push({ + id: notification_id, + file_name: build, + monitoring_type: "orbitMonitoring", + status: "notified", + notifiedTo: teamsNotificationDetails.recipients, + notification_channel: "msTeams", + application_id, + notification_reason: notificationDetails.value, + monitoring_id: id, + }); + } catch (err) { + logger.error(err); + } + } + } + + // Add sent notifications to notification table + if (sentNotifications.length > 0) { + try { + await monitoring_notifications.bulkCreate(sentNotifications); + } catch (err) { + logger.error(err); + } + } + } catch (err) { + logger.error(err); + } finally { + parentPort ? parentPort.postMessage("done") : process.exit(0); + } +})(); diff --git a/server/migrations/20230629184052-add_metaData_to_monitoring_notification_table.js b/server/migrations/20230629184052-add_metaData_to_monitoring_notification_table.js index 17bdfef2b..d4901ed66 100644 --- a/server/migrations/20230629184052-add_metaData_to_monitoring_notification_table.js +++ b/server/migrations/20230629184052-add_metaData_to_monitoring_notification_table.js @@ -33,4 +33,4 @@ module.exports = { throw err; } }, -}; +}; diff --git a/server/migrations/20230906000000-create-OrbitMonitoring.js b/server/migrations/20230906000000-create-OrbitMonitoring.js new file mode 100644 index 000000000..63bd8be89 --- /dev/null +++ b/server/migrations/20230906000000-create-OrbitMonitoring.js @@ -0,0 +1,89 @@ +"use strict"; +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable("orbitMonitoring", { + id: { + allowNull: false, + primaryKey: true, + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + }, + application_id: { + type: Sequelize.UUID, + references: { + model: "application", + key: "id", + }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + }, + name: { + allowNull: false, + type: Sequelize.DataTypes.STRING, + }, + cron: { + allowNull: false, + type: Sequelize.DataTypes.STRING, + }, + isActive: { + allowNull: false, + type: Sequelize.DataTypes.BOOLEAN, + defaultValue: false, + }, + build: { + allowNull: false, + type: Sequelize.DataTypes.STRING, + defaultValue: false, + }, + severityCode: { + allowNull: false, + type: Sequelize.DataTypes.TINYINT, + defaultValue: 0, + }, + businessUnit: { + allowNull: false, + type: Sequelize.DataTypes.STRING, + defaultValue: false, + }, + product: { + allowNull: false, + type: Sequelize.DataTypes.STRING, + defaultValue: false, + }, + host: { + allowNull: false, + type: Sequelize.DataTypes.STRING, + defaultValue: false, + }, + primaryContact: { + allowNull: true, + type: Sequelize.DataTypes.STRING, + defaultValue: false, + }, + secondaryContact: { + allowNull: true, + type: Sequelize.DataTypes.STRING, + defaultValue: false, + }, + metaData: { + type: Sequelize.JSON, + allowNull: true, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + deletedAt: { + allowNull: true, + type: Sequelize.DATE, + }, + }); + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable("orbitMonitoring"); + }, +}; diff --git a/server/migrations/20230906195957-create-orbit-builds.js b/server/migrations/20230906195957-create-orbit-builds.js new file mode 100644 index 000000000..50b065016 --- /dev/null +++ b/server/migrations/20230906195957-create-orbit-builds.js @@ -0,0 +1,66 @@ +"use strict"; +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable("orbitBuilds", { + id: { + allowNull: false, + primaryKey: true, + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + }, + application_id: { + type: Sequelize.UUID, + references: { + model: "application", + key: "id", + }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + }, + monitoring_id: { + type: Sequelize.UUID, + references: { + model: "orbitMonitoring", + key: "id", + }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + }, + type: { + type: Sequelize.STRING, + allowNull: false, + }, + build_id: { + type: Sequelize.STRING, + allowNull: false, + }, + wuid: { + type: Sequelize.STRING, + allowNull: false, + }, + name: { + allowNull: false, + type: Sequelize.DataTypes.STRING, + }, + metaData: { + type: Sequelize.JSON, + allowNull: true, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + deletedAt: { + allowNull: true, + type: Sequelize.DATE, + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable("orbitBuilds"); + }, +}; diff --git a/server/migrations/20230907145012-create-integrations.js b/server/migrations/20230907145012-create-integrations.js new file mode 100644 index 000000000..40f14238e --- /dev/null +++ b/server/migrations/20230907145012-create-integrations.js @@ -0,0 +1,43 @@ +"use strict"; +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable("integrations", { + id: { + allowNull: false, + primaryKey: true, + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + }, + application_id: { + type: Sequelize.UUID, + }, + name: { + type: Sequelize.STRING, + }, + description: { + type: Sequelize.STRING, + }, + active: { + type: Sequelize.BOOLEAN, + }, + config: { + type: Sequelize.JSON, + }, + metaData: { + type: Sequelize.JSON, + allowNull: true, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable("integrations"); + }, +}; diff --git a/server/models/integrations.js b/server/models/integrations.js new file mode 100644 index 000000000..ba3e5ddc5 --- /dev/null +++ b/server/models/integrations.js @@ -0,0 +1,53 @@ +"use strict"; +module.exports = (sequelize, DataTypes) => { + const integrations = sequelize.define( + "integrations", + { + id: { + primaryKey: true, + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + allowNull: false, + autoIncrement: false, + }, + name: { + allowNull: false, + type: DataTypes.STRING, + unique: false, + }, + description: { + allowNull: false, + type: DataTypes.STRING, + unique: false, + }, + active: { + allowNull: false, + type: DataTypes.BOOLEAN, + }, + config: { + type: DataTypes.JSON, + allowNull: true, + }, + metaData: { + type: DataTypes.JSON, + allowNull: true, + }, + application_id: { + allowNull: false, + type: DataTypes.UUID, + }, + }, + { freezeTableName: true } + ); + integrations.associate = function (models) { + // Define association here + integrations.belongsTo(models.application, { + foreignKey: "application_id", + }); + integrations.hasMany(models.monitoring_notifications, { + foreignKey: "application_id", + onDelete: "CASCADE", + }); + }; + return integrations; +}; diff --git a/server/models/orbitMonitoring.js b/server/models/orbitMonitoring.js new file mode 100644 index 000000000..f1ccaa020 --- /dev/null +++ b/server/models/orbitMonitoring.js @@ -0,0 +1,76 @@ +"use strict"; +module.exports = (sequelize, DataTypes) => { + const orbitMonitoring = sequelize.define( + "orbitMonitoring", + { + id: { + primaryKey: true, + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + allowNull: false, + autoIncrement: false, + }, + application_id: { + allowNull: false, + type: DataTypes.UUID, + }, + name: { + allowNull: false, + type: DataTypes.STRING, + unique: true, + }, + cron: { + allowNull: true, + type: DataTypes.STRING, + }, + build: { + allowNull: false, + type: DataTypes.STRING, + }, + businessUnit: { + allowNull: false, + type: DataTypes.STRING, + }, + product: { + allowNull: false, + type: DataTypes.STRING, + }, + severityCode: { + allowNull: false, + type: DataTypes.TINYINT, + }, + host: { + allowNull: false, + type: DataTypes.STRING, + }, + primaryContact: { + allowNull: true, + type: DataTypes.STRING, + }, + secondaryContact: { + allowNull: true, + type: DataTypes.STRING, + }, + metaData: { + type: DataTypes.JSON, + allowNull: true, + }, + isActive: { + type: DataTypes.BOOLEAN, + allowNull: true, + }, + }, + { paranoid: true, freezeTableName: true } + ); + orbitMonitoring.associate = function (models) { + // Define association here + orbitMonitoring.belongsTo(models.application, { + foreignKey: "application_id", + }); + orbitMonitoring.hasMany(models.monitoring_notifications, { + foreignKey: "application_id", + onDelete: "CASCADE", + }); + }; + return orbitMonitoring; +}; diff --git a/server/models/orbitbuilds.js b/server/models/orbitbuilds.js new file mode 100644 index 000000000..c38d676c3 --- /dev/null +++ b/server/models/orbitbuilds.js @@ -0,0 +1,60 @@ +"use strict"; +module.exports = (sequelize, DataTypes) => { + const orbitBuilds = sequelize.define( + "orbitBuilds", + { + id: { + primaryKey: true, + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + allowNull: false, + autoIncrement: false, + }, + name: { + allowNull: false, + type: DataTypes.STRING, + unique: true, + }, + application_id: { + allowNull: false, + type: DataTypes.UUID, + }, + monitoring_id: { + allowNull: true, + type: DataTypes.UUID, + }, + type: { + type: DataTypes.STRING, + allowNull: false, + }, + wuid: { + type: DataTypes.STRING, + allowNull: false, + }, + metaData: { + type: DataTypes.JSON, + allowNull: true, + }, + build_id: { + type: DataTypes.STRING, + allowNull: false, + }, + metaData: { + type: DataTypes.JSON, + allowNull: true, + }, + }, + { paranoid: true, freezeTableName: true } + ); + orbitBuilds.associate = function (models) { + // Define association here + orbitBuilds.belongsTo(models.application, { + foreignKey: "application_id", + }); + orbitBuilds.hasMany(models.monitoring_notifications, { + foreignKey: "application_id", + onDelete: "CASCADE", + }); + }; + return orbitBuilds; +}; diff --git a/server/package-lock.json b/server/package-lock.json index 9f04eb1cb..5007186e2 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -26,6 +26,7 @@ "lodash": "^4.17.20", "moment": "^2.29.4", "morgan": "^1.10.0", + "mssql": "^9.1.3", "multer": "^1.4.5-lts.1", "mysql2": "^1.7.0", "nodemailer": "^6.7.1", @@ -40,6 +41,7 @@ "sequelize-cli": "^6.4.1", "simple-git": "^3.6.0", "socket.io": "^4.6.1", + "sqlstring": "^2.3.3", "tmp": "^0.1.0", "uuid": "^3.4.0", "winston": "^3.7.2" @@ -63,6 +65,281 @@ "node": ">=6.0.0" } }, + "node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", + "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.3.tgz", + "integrity": "sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", + "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", + "dependencies": { + "@azure/abort-controller": "^1.0.4", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.3.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.0.tgz", + "integrity": "sha512-+MnSB0vGZjszSzr5AW8z93/9fkDu2RLtWmAN8gskURq7EW2sSwqy8jZa0V26rjuBVkwhdA3Hw8z3VWoeBUOw+A==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "form-data": "^4.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", + "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz", + "integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-2.1.0.tgz", + "integrity": "sha512-BPDz1sK7Ul9t0l9YKLEa8PHqWU4iCfhGJ+ELJl6c8CP3TpJt2urNCbm0ZHsthmxRsYoMPbz2Dvzj30zXZVmAFw==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^2.26.0", + "@azure/msal-common": "^7.0.0", + "@azure/msal-node": "^1.10.0", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/identity/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@azure/identity/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@azure/identity/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@azure/keyvault-keys": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.7.1.tgz", + "integrity": "sha512-zfmlZQCw1Yz+aPhgZmWOYBUzaKmfBzR2yceAE4S6hKDl7YZraTguuXmtFbCqjRvpz+pIMKAK25fENay9mFy1hQ==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^1.3.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "2.38.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.38.1.tgz", + "integrity": "sha512-NROo7mLpw7aWtj8tWy9ZPz3WWiudwVAOIDZ1K3PPrjDAA4kFYayWlbZiJl1T1sD5Oqwa6FOtwzFSvuUj1CWp6Q==", + "dependencies": { + "@azure/msal-common": "13.2.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-browser/node_modules/@azure/msal-common": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.2.1.tgz", + "integrity": "sha512-9CtyVdDtAOw+raemKg8gdBuE7gleObgSb7p4bzMIlUt8eM69/Gaow7uqr1gK3jLYINSrss32OZW8mBbdgVLiHg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.6.0.tgz", + "integrity": "sha512-XqfbglUTVLdkHQ8F9UQJtKseRr3sSnr9ysboxtoswvaMVaEfvyLtMoHv9XdKUfOc0qKGzNgRFd9yRjIWVepl6Q==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.18.1.tgz", + "integrity": "sha512-B4kUOWJoN4vD8b3pGJ9Q9mIZhaDb8EnQM1aN0x1otlQgTfzDvEk6rWc6fy8uGdtXqcNddBtiXdc4oRiItroVkA==", + "dependencies": { + "@azure/msal-common": "13.2.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": "10 || 12 || 14 || 16 || 18" + } + }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.2.1.tgz", + "integrity": "sha512-9CtyVdDtAOw+raemKg8gdBuE7gleObgSb7p4bzMIlUt8eM69/Gaow7uqr1gK3jLYINSrss32OZW8mBbdgVLiHg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -1181,6 +1458,11 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, + "node_modules/@js-joda/core": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.5.3.tgz", + "integrity": "sha512-7dqNYwG8gCt4hfg5PKgM7xLEcgSBcx/UgC92OMnhMmvAnq11QzDFPrxUkNR/u5kn17WWLZ8beZ4A3Qrz4pZcmQ==" + }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -1223,6 +1505,19 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, + "node_modules/@tediousjs/connection-string": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.4.4.tgz", + "integrity": "sha512-Qssn7gmOILmqD0zkfA09YyFd52UajWYkLTTSi4Dx/XZaUuVcx4W4guv2rAVc5mm8wYRdonmG/HfFH3PS6izXAg==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@types/babel__core": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", @@ -1510,11 +1805,42 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -1555,6 +1881,17 @@ "node": ">= 4.0.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1766,6 +2103,29 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1987,7 +2347,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -2476,6 +2835,29 @@ "node": ">=0.10.0" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2710,6 +3092,107 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/es-abstract": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-aggregate-error": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.9.tgz", + "integrity": "sha512-fvnX40sb538wdU6r4s35cq4EY6Lr09Upj40BEVem4LEsuW8XgQep9yD5Q1U2KftokNp1rWODFJ2qwZSsAjFpbg==", + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "function-bind": "^1.1.1", + "functions-have-names": "^1.2.3", + "get-intrinsic": "^1.1.3", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es5-ext": { "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", @@ -2802,6 +3285,14 @@ "node": ">=6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3101,6 +3592,14 @@ } } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -3206,6 +3705,31 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -3235,7 +3759,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -3267,13 +3790,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dependencies": { - "assert-plus": "^1.0.0" - } + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": "^1.0.0" + } }, "node_modules/glob": { "version": "6.0.4", @@ -3333,6 +3871,31 @@ "node": ">=4" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.9", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", @@ -3370,6 +3933,14 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -3379,11 +3950,21 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3395,7 +3976,20 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, "engines": { "node": ">= 0.4" }, @@ -3433,6 +4027,19 @@ "node": ">= 0.6" } }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -3567,6 +4174,19 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3575,11 +4195,35 @@ "node": ">= 0.10" } }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3592,6 +4236,32 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -3603,6 +4273,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", @@ -3650,6 +4348,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3659,6 +4368,20 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -3669,6 +4392,32 @@ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3680,6 +4429,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-string-and-not-blank": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/is-string-and-not-blank/-/is-string-and-not-blank-0.0.2.tgz", @@ -3696,6 +4459,34 @@ "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==" }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -3712,6 +4503,28 @@ "node": ">=0.10.0" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4527,6 +5340,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4559,6 +5377,11 @@ "node": ">=4" } }, + "node_modules/jsbi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" + }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -5086,6 +5909,33 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/mssql": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-9.1.3.tgz", + "integrity": "sha512-oXs2lJ1vKUe2s0twCdcdKnqATTVaIswzpSiGnUjMIhV6Sip9vEDuYt3dCoVWXXNuPJ5iFIqLxvagw4Hrz6xR4A==", + "dependencies": { + "@tediousjs/connection-string": "^0.4.1", + "commander": "^11.0.0", + "debug": "^4.3.3", + "rfdc": "^1.3.0", + "tarn": "^3.0.2", + "tedious": "^15.0.1" + }, + "bin": { + "mssql": "bin/mssql" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mssql/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/multer": { "version": "1.4.5-lts.1", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", @@ -5172,9 +6022,9 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", "optional": true }, "node_modules/nanoclone": { @@ -5182,6 +6032,11 @@ "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" }, + "node_modules/native-duplexpair": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", + "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5210,6 +6065,11 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -5397,7 +6257,31 @@ "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5452,6 +6336,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -5906,6 +6806,22 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -6036,6 +6952,11 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, "node_modules/rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", @@ -6048,6 +6969,28 @@ "rimraf": "bin.js" } }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6059,6 +7002,19 @@ "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "optional": true }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-stable-stringify": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", @@ -6298,7 +7254,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -6444,9 +7399,9 @@ "dev": true }, "node_modules/sqlstring": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz", - "integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", "engines": { "node": ">= 0.6" } @@ -6511,6 +7466,15 @@ "node": ">=0.10.0" } }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -6553,6 +7517,48 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6692,6 +7698,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/tedious": { + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-15.1.3.tgz", + "integrity": "sha512-166EpRm5qknwhEisjZqz/mF7k14fXKJYHRg6XiAXVovd/YkyHJ3SG4Ppy89caPaNFfRr7PVYe+s4dAvKaCMFvw==", + "dependencies": { + "@azure/identity": "^2.0.4", + "@azure/keyvault-keys": "^4.4.0", + "@js-joda/core": "^5.2.0", + "bl": "^5.0.0", + "es-aggregate-error": "^1.0.8", + "iconv-lite": "^0.6.3", + "js-md4": "^0.3.2", + "jsbi": "^4.3.0", + "native-duplexpair": "^1.0.0", + "node-abort-controller": "^3.0.1", + "punycode": "^2.1.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/tedious/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tedious/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6942,6 +7994,67 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -6958,6 +8071,20 @@ "node": ">=6.0.0" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -7189,6 +8316,39 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/winston": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", @@ -7451,6 +8611,229 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-auth": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", + "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.3.tgz", + "integrity": "sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-http-compat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", + "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", + "requires": { + "@azure/abort-controller": "^1.0.4", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.3.0" + } + }, + "@azure/core-lro": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-rest-pipeline": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.0.tgz", + "integrity": "sha512-+MnSB0vGZjszSzr5AW8z93/9fkDu2RLtWmAN8gskURq7EW2sSwqy8jZa0V26rjuBVkwhdA3Hw8z3VWoeBUOw+A==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "form-data": "^4.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@azure/core-tracing": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", + "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-util": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz", + "integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/identity": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-2.1.0.tgz", + "integrity": "sha512-BPDz1sK7Ul9t0l9YKLEa8PHqWU4iCfhGJ+ELJl6c8CP3TpJt2urNCbm0ZHsthmxRsYoMPbz2Dvzj30zXZVmAFw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^2.26.0", + "@azure/msal-common": "^7.0.0", + "@azure/msal-node": "^1.10.0", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + }, + "dependencies": { + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@azure/keyvault-keys": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.7.1.tgz", + "integrity": "sha512-zfmlZQCw1Yz+aPhgZmWOYBUzaKmfBzR2yceAE4S6hKDl7YZraTguuXmtFbCqjRvpz+pIMKAK25fENay9mFy1hQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^1.3.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/msal-browser": { + "version": "2.38.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.38.1.tgz", + "integrity": "sha512-NROo7mLpw7aWtj8tWy9ZPz3WWiudwVAOIDZ1K3PPrjDAA4kFYayWlbZiJl1T1sD5Oqwa6FOtwzFSvuUj1CWp6Q==", + "requires": { + "@azure/msal-common": "13.2.1" + }, + "dependencies": { + "@azure/msal-common": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.2.1.tgz", + "integrity": "sha512-9CtyVdDtAOw+raemKg8gdBuE7gleObgSb7p4bzMIlUt8eM69/Gaow7uqr1gK3jLYINSrss32OZW8mBbdgVLiHg==" + } + } + }, + "@azure/msal-common": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.6.0.tgz", + "integrity": "sha512-XqfbglUTVLdkHQ8F9UQJtKseRr3sSnr9ysboxtoswvaMVaEfvyLtMoHv9XdKUfOc0qKGzNgRFd9yRjIWVepl6Q==" + }, + "@azure/msal-node": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.18.1.tgz", + "integrity": "sha512-B4kUOWJoN4vD8b3pGJ9Q9mIZhaDb8EnQM1aN0x1otlQgTfzDvEk6rWc6fy8uGdtXqcNddBtiXdc4oRiItroVkA==", + "requires": { + "@azure/msal-common": "13.2.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "dependencies": { + "@azure/msal-common": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.2.1.tgz", + "integrity": "sha512-9CtyVdDtAOw+raemKg8gdBuE7gleObgSb7p4bzMIlUt8eM69/Gaow7uqr1gK3jLYINSrss32OZW8mBbdgVLiHg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, "@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -8311,6 +9694,11 @@ } } }, + "@js-joda/core": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.5.3.tgz", + "integrity": "sha512-7dqNYwG8gCt4hfg5PKgM7xLEcgSBcx/UgC92OMnhMmvAnq11QzDFPrxUkNR/u5kn17WWLZ8beZ4A3Qrz4pZcmQ==" + }, "@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -8353,6 +9741,16 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, + "@tediousjs/connection-string": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.4.4.tgz", + "integrity": "sha512-Qssn7gmOILmqD0zkfA09YyFd52UajWYkLTTSi4Dx/XZaUuVcx4W4guv2rAVc5mm8wYRdonmG/HfFH3PS6izXAg==" + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" + }, "@types/babel__core": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", @@ -8603,11 +10001,33 @@ "sprintf-js": "~1.0.2" } }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + } + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -8642,6 +10062,11 @@ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -8799,6 +10224,28 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "requires": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -8970,7 +10417,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -9345,6 +10791,20 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -9534,6 +10994,86 @@ } } }, + "es-abstract": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "requires": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" + } + }, + "es-aggregate-error": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.9.tgz", + "integrity": "sha512-fvnX40sb538wdU6r4s35cq4EY6Lr09Upj40BEVem4LEsuW8XgQep9yD5Q1U2KftokNp1rWODFJ2qwZSsAjFpbg==", + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "function-bind": "^1.1.1", + "functions-have-names": "^1.2.3", + "get-intrinsic": "^1.1.3", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0" + } + }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es5-ext": { "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", @@ -9614,6 +11154,11 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9860,6 +11405,14 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==" }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -9936,6 +11489,22 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, "generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -9959,7 +11528,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -9979,6 +11547,15 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -10032,6 +11609,22 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "requires": { + "define-properties": "^1.1.3" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.9", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", @@ -10059,23 +11652,42 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } }, "hexoid": { "version": "1.0.0", @@ -10101,6 +11713,16 @@ "toidentifier": "1.0.1" } }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -10193,16 +11815,44 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "requires": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + } + }, "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -10212,6 +11862,20 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, "is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -10220,6 +11884,19 @@ "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", @@ -10252,12 +11929,25 @@ "is-glob": "^2.0.0" } }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -10268,11 +11958,36 @@ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-string-and-not-blank": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/is-string-and-not-blank/-/is-string-and-not-blank-0.0.2.tgz", @@ -10286,6 +12001,22 @@ "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==" }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "requires": { + "which-typed-array": "^1.1.11" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -10299,6 +12030,22 @@ "is-invalid-path": "^0.1.0" } }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -10929,6 +12676,11 @@ } } }, + "js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10953,6 +12705,11 @@ } } }, + "jsbi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -11392,6 +13149,26 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "mssql": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-9.1.3.tgz", + "integrity": "sha512-oXs2lJ1vKUe2s0twCdcdKnqATTVaIswzpSiGnUjMIhV6Sip9vEDuYt3dCoVWXXNuPJ5iFIqLxvagw4Hrz6xR4A==", + "requires": { + "@tediousjs/connection-string": "^0.4.1", + "commander": "^11.0.0", + "debug": "^4.3.3", + "rfdc": "^1.3.0", + "tarn": "^3.0.2", + "tedious": "^15.0.1" + }, + "dependencies": { + "commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==" + } + } + }, "multer": { "version": "1.4.5-lts.1", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", @@ -11467,9 +13244,9 @@ } }, "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", "optional": true }, "nanoclone": { @@ -11477,6 +13254,11 @@ "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" }, + "native-duplexpair": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", + "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -11499,6 +13281,11 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, + "node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -11638,8 +13425,23 @@ "object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } }, "on-finished": { "version": "2.3.0", @@ -11679,6 +13481,16 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -12018,6 +13830,16 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, + "regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + } + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -12112,6 +13934,11 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", @@ -12121,6 +13948,24 @@ "glob": "^6.0.1" } }, + "safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -12132,6 +13977,16 @@ "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "optional": true }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, "safe-stable-stringify": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", @@ -12304,7 +14159,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -12427,9 +14281,9 @@ "dev": true }, "sqlstring": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz", - "integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==" + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==" }, "sshpk": { "version": "1.17.0", @@ -12471,6 +14325,11 @@ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, + "stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" + }, "streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -12504,6 +14363,36 @@ "strip-ansi": "^6.0.1" } }, + "string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -12600,6 +14489,45 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==" + }, + "tedious": { + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-15.1.3.tgz", + "integrity": "sha512-166EpRm5qknwhEisjZqz/mF7k14fXKJYHRg6XiAXVovd/YkyHJ3SG4Ppy89caPaNFfRr7PVYe+s4dAvKaCMFvw==", + "requires": { + "@azure/identity": "^2.0.4", + "@azure/keyvault-keys": "^4.4.0", + "@js-joda/core": "^5.2.0", + "bl": "^5.0.0", + "es-aggregate-error": "^1.0.8", + "iconv-lite": "^0.6.3", + "js-md4": "^0.3.2", + "jsbi": "^4.3.0", + "native-duplexpair": "^1.0.0", + "node-abort-controller": "^3.0.1", + "punycode": "^2.1.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + } + } + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -12799,6 +14727,49 @@ "mime-types": "~2.1.24" } }, + "typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -12812,6 +14783,17 @@ "bluebird": "^3.7.2" } }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -12994,6 +14976,30 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "winston": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", diff --git a/server/package.json b/server/package.json index c777a5105..20123febe 100644 --- a/server/package.json +++ b/server/package.json @@ -4,7 +4,7 @@ "description": "", "main": "server.js", "scripts": { - "test": "jest --silent --watchAll", + "test": "jest --silent", "bootstrap-server": "(npm install) && (npm run createSchema) && (npm run migrations) && (npm run seeds) && (nodemon server.js)", "createSchema": "npx sequelize db:create tombolo", "migrations": "npx sequelize db:migrate", @@ -30,6 +30,7 @@ "lodash": "^4.17.20", "moment": "^2.29.4", "morgan": "^1.10.0", + "mssql": "^9.1.3", "multer": "^1.4.5-lts.1", "mysql2": "^1.7.0", "nodemailer": "^6.7.1", @@ -44,6 +45,7 @@ "sequelize-cli": "^6.4.1", "simple-git": "^3.6.0", "socket.io": "^4.6.1", + "sqlstring": "^2.3.3", "tmp": "^0.1.0", "uuid": "^3.4.0", "winston": "^3.7.2" diff --git a/server/routes/integrations/read.js b/server/routes/integrations/read.js new file mode 100644 index 000000000..8000d8e45 --- /dev/null +++ b/server/routes/integrations/read.js @@ -0,0 +1,108 @@ +const models = require("../../models"); +const integrations = models.integrations; +let application = models.application; +const express = require("express"); +const router = express.Router(); +const path = require("path"); +const fs = require("fs"); +const rootENV = path.join(process.cwd(), "..", ".env"); +const serverENV = path.join(process.cwd(), ".env"); +const ENVPath = fs.existsSync(rootENV) ? rootENV : serverENV; +const { param, validationResult } = require("express-validator"); +const validatorUtil = require("../../utils/validator"); +require("dotenv").config({ path: ENVPath }); + +router.get( + "/get/:application_id", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { application_id } = req.params; + if (!application_id) throw Error("Invalid app ID"); + + const result = await integrations.findAll({ + where: { + application_id, + }, + }); + + res.status(200).send(result); + } catch (err) { + // ... error checks + console.log(err); + } + } +); + +//activate or deactive integration +router.put( + "/toggle/:application_id/:name", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { application_id, name } = req.params; + + const integration = await integrations.findOne({ + where: { application_id, name }, + raw: true, + }); + + const { active, id } = integration; + + // flipping Active + await integrations.update({ active: !active }, { where: { id } }); + + res.status(200).send("integration toggled succesfully"); + } catch (err) { + // ... error checks + console.log(err); + } + } +); + +//update integration notifications +router.put( + "/update/:application_id/:name", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { application_id, name } = req.params; + + if (!application_id) throw Error("Invalid app ID"); + const oldintegration = await integrations.findOne({ + where: { application_id, name }, + raw: true, + }); + + const { id } = oldintegration; + + // adjusting + await integrations.update( + { metaData: req.body.notifications, config: req.body.active }, + { where: { id } } + ); + + res.status(200).send("integration updated succesfully"); + } catch (err) { + // ... error checks + console.log(err); + } + } +); + +module.exports = router; diff --git a/server/routes/notifications/read.js b/server/routes/notifications/read.js index a3e79c886..185ec62b6 100644 --- a/server/routes/notifications/read.js +++ b/server/routes/notifications/read.js @@ -64,15 +64,13 @@ router.get( "/:applicationId", [param("applicationId").isUUID(4).withMessage("Invalid application id")], async (req, res) => { - const errors = validationResult(req).formatWith( - validatorUtil.errorFormatter - ); + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); try { - if (!errors.isEmpty()) - return res - .status(422) - .json({ success: false, errors: errors.array() }); + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); const { applicationId: application_id } = req.params; if (!application_id) throw Error("Invalid app ID"); const notifications = await monitoring_notifications.findAll({ diff --git a/server/routes/orbit/read.js b/server/routes/orbit/read.js new file mode 100644 index 000000000..b63c657a7 --- /dev/null +++ b/server/routes/orbit/read.js @@ -0,0 +1,840 @@ +const models = require("../../models"); +const orbitBuilds = models.orbitBuilds; +const orbitMonitoring = models.orbitMonitoring; +const express = require("express"); +const { param, body, validationResult } = require("express-validator"); +const { Op } = require("sequelize"); +const moment = require("moment"); +const router = express.Router(); +const path = require("path"); +const fs = require("fs"); +const rootENV = path.join(process.cwd(), "..", ".env"); +const serverENV = path.join(process.cwd(), ".env"); +const ENVPath = fs.existsSync(rootENV) ? rootENV : serverENV; +const validatorUtil = require("../../utils/validator"); +const monitoring_notifications = models.monitoring_notifications; +const notificationTemplate = require("../../jobs/messageCards/notificationTemplate"); +const { notify } = require("../notifications/email-notification"); +const { v4: uuidv4 } = require("uuid"); +const axios = require("axios"); +const SqlString = require("sqlstring"); + +const jobScheduler = require("../../job-scheduler"); + +const sql = require("mssql"); +const db = require("../../models"); + +const dbConfig = { + server: process.env.ORBIT_DB, + database: process.env.ORBIT_DB_NAME, + user: process.env.ORBIT_DB_USER, + password: process.env.ORBIT_DB_PWD, + port: parseInt(process.env.ORBIT_DB_PORT), + trustServerCertificate: true, +}; + +const fidoDbConfig = { + server: process.env.FIDO_DB, + database: process.env.FIDO_DB_NAME, + user: process.env.FIDO_DB_USER, + password: process.env.FIDO_DB_PWD, + port: parseInt(process.env.FIDO_DB_PORT), + trustServerCertificate: true, +}; + +const runSQLQuery = async (query, config) => { + try { + await sql.connect(config); + + const result = await sql.query(query); + + //need this close to fix bug where it was only contacting the first server + sql.close(); + return result; + } catch (err) { + console.log(err); + return { + err, + message: "There was an issue contacting the server", + }; + } +}; + +require("dotenv").config({ path: ENVPath }); + +//create one monitoring +//TODO get workunits from past 2 weeks as well in orbitbuilds table +router.post( + "/", + [ + body("application_id").isUUID(4).withMessage("Invalid application id"), + body("cron").custom((value) => { + const valArray = value.split(" "); + if (valArray.length > 5) { + throw new Error( + `Expected number of cron parts 5, received ${valArray.length}` + ); + } else { + return Promise.resolve("Good to go"); + } + }), + ], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + + // get last status and WU to store against future checks + const query = `select TOP 1 HpccWorkUnit as 'WorkUnit', Name as 'Build', DateUpdated as 'Date', Status_Code as 'Status' from DimBuildInstance where Name = ${SqlString.escape( + req.body.build + )} order by Date desc`; + + const wuResult = await runSQLQuery(query, dbConfig); + + if (wuResult?.err) { + throw Error(result.message); + } + + //destructure out of recordset and place inside of new metaData + const { WorkUnit, Date, Status } = wuResult.recordset[0]; + + const metaData = req.body.metaData; + + metaData.lastWorkUnit = { + lastWorkUnit: WorkUnit, + lastWorkUnitDate: Date, + lastWorkUnitStatus: Status, + }; + + //null out isActive in metaData to reduce noise + metaData.isActive = null; + + // TODO transform data before sending in for easier updates + let newBuildData = { + application_id: req.body.application_id, + cron: req.body.cron, + name: req.body.name, + build: req.body.build, + severityCode: req.body.severityCode, + product: req.body.product, + businessUnit: req.body.businessUnit, + host: req.body.host, + primaryContact: req.body.primaryContact, + secondaryContact: req.body.secondaryContact, + metaData: metaData, + isActive: req.body.isActive, + }; + + const newOrbitMonitoring = await orbitMonitoring.create(newBuildData); + + const { isActive } = req.body; + + //Add monitoring to bree if start monitoring now is checked + if (isActive) { + const schedularOptions = { + orbitMonitoring_id: newOrbitMonitoring.dataValues.id, + cron: newOrbitMonitoring.cron, + }; + + jobScheduler.createOrbitMonitoringJob(schedularOptions); + } + + res.status(201).send(newOrbitMonitoring); + } catch (error) { + console.log(error); + res + .status(500) + .json({ message: "Unable to save Orbit monitoring details" }); + } + } +); + +//get all monitorings +router.get( + "/allMonitorings/:application_id", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { application_id } = req.params; + + const result = await orbitMonitoring.findAll({ + where: { + application_id, + }, + }); + + res.status(200).send(result); + } catch (err) { + // ... error checks + console.log(err); + } + } +); + +//get all +router.get( + "/allMonitoring/:application_id", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { application_id } = req.params; + if (!application_id) throw Error("Invalid app ID"); + const result = await orbitMonitoring.findAll({ + where: { + application_id, + }, + }); + + res.status(200).send(result); + } catch (err) { + // ... error checks + console.log(err); + } + } +); + +//search Database for builds with keyword +router.get( + "/search/:application_id/:keyword", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + [ + param("keyword") + .matches(/^[a-zA-Z0-9_.\-:\*\? ]*$/) + .withMessage("Invalid keyword"), + ], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + console.log(errors); + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { application_id, keyword } = req.params; + if (!application_id) throw Error("Invalid app ID"); + + const keywordEscaped = SqlString.escape("%" + keyword + "%"); + + const query = `select Name from DimBuildInstance where Name like ${keywordEscaped} and Name not like 'Scrub%' and EnvironmentName = 'Insurance' order by Name asc`; + + const result = await runSQLQuery(query, dbConfig); + + if (result.err) { + throw Error(result.message); + } + + const uniqueNames = []; + + const unique = result.recordset.filter((element) => { + const isDuplicate = uniqueNames.includes(element.Name); + + if (!isDuplicate) { + uniqueNames.push(element.Name); + + return true; + } + + return false; + }); + + res.status(200).json(unique); + } catch (err) { + // ... error checks + + console.log(err); + res + .status(400) + .send("There was an issue contacting the orbit reports server"); + } + } +); + +/* get single build */ +router.get( + "/getOrbitBuildDetails/:buildName", + [ + param("buildName") + .matches(/^[a-zA-Z0-9_.\-:\*\? ]*$/) + .withMessage("Invalid build name"), + ], + + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + + const { buildName } = req.params; + + //connect to db + + const query = `select Top 1 EnvironmentName, Name, Status_DateCreated, HpccWorkUnit, Status_Code, Substatus_Code, BuildInstanceIdKey from DimBuildInstance where Name = ${SqlString.escape( + buildName + )} order by Status_DateCreated desc`; + + const result = await runSQLQuery(query, dbConfig); + + if (result.err) { + throw Error(result.message); + } + + res.json(result?.recordset[0]); + } catch (err) { + // ... error checks + + console.log(err); + res + .status(400) + .send("There was an issue contacting the orbit reports server"); + } + } +); + +//update orbit monitoring +router.put( + "/", + [ + body("application_id").isUUID(4).withMessage("Invalid application id"), + body("cron").custom((value) => { + const valArray = value.split(" "); + if (valArray.length > 5) { + throw new Error( + `Expected number of cron parts 5, received ${valArray.length}` + ); + } else { + return Promise.resolve("Good to go"); + } + }), + ], + async (req, res) => { + try { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + + const oldInfo = await orbitMonitoring.findOne({ + where: { id: req.body.id }, + raw: true, + }); + + let newInfo = req.body; + + //destructure info out of sent info + const { + id, + name, + build, + notifyCondition, + severityCode, + product, + businessUnit, + host, + isActive, + application_id, + primaryContact, + secondaryContact, + cron, + metaData: { lastMonitored, monitoringCondition }, + } = newInfo; + + //CODEQL FIX + //----------------------- + let notificationChannels = req.body.notificationChannels; + + if (!(notificationChannels instanceof Array)) { + return []; + } + //----------------------- + + //build out notifications object for storing inside metadata + let emails, msTeamsGroups; + if (notificationChannels.includes("eMail")) { + emails = newInfo.emails; + } + if (notificationChannels.includes("msTeams")) { + msTeamsGroups = newInfo.msTeamsGroups; + } + + let notifications = []; + + for (let i = 0; i < notificationChannels.length; i++) { + if (notificationChannels[i] === "eMail") { + notifications.push({ channel: "eMail", recipients: emails }); + } + if (notificationChannels[i] === "msTeams") { + notifications.push({ + channel: "msTeams", + recipients: msTeamsGroups, + }); + } + } + const escapedBuild = SqlString.escape(build); + + //get most recent work unit for storage + // get last status and WU to store against future checks + const query = `select TOP 1 HpccWorkUnit as 'WorkUnit', Name as 'Build', DateUpdated as 'Date', Status_Code as 'Status' from DimBuildInstance where Name = ${escapedBuild} + order by Date desc`; + + const wuResult = await runSQLQuery(query, dbConfig); + + if (wuResult.err) { + throw Error(result.message); + } + + //destructure out of recordset and place inside of new metaData + const { WorkUnit, Date, Status } = wuResult.recordset[0]; + + //set data fields + newInfo = { + id, + name, + cron, + isActive, + build, + severityCode, + product, + businessUnit, + application_id, + host, + primaryContact, + secondaryContact, + metaData: { + lastWorkUnit: { + lastWorkUnit: WorkUnit, + lastWorkUnitDate: Date, + lastWorkUnitStatus: Status, + }, + lastMonitored, + notifications, + severityCode, + monitoringCondition, + }, + }; + // ------------------------------------------------------- + + await orbitMonitoring.update(newInfo, { where: { id } }); + + // If start monitoring was changed to TRUE + if (isActive && oldInfo.isActive === 0) { + const schedularOptions = { + orbitMonitoring_id: id, + cron: newOrbitMonitoring.cron, + }; + + jobScheduler.createOrbitMonitoringJob(schedularOptions); + } + + // If start monitoring was changed to FALSE + if (!isActive && oldInfo.isActive === 1) { + await jobScheduler.removeJobFromScheduler(`Orbit Monitoring - ${id}`); + } + + // if cron has changed + if (oldInfo.cron != cron) { + const allBreeJobs = jobScheduler.getAllJobs(); + const jobName = `Orbit Monitoring - ${id}`; + for (let job of allBreeJobs) { + if (job.name === jobName) { + await jobScheduler.removeJobFromScheduler(jobName); + await jobScheduler.createOrbitMonitoringJob({ + orbitMonitoring_id: id, + + cron: cron, + }); + } + } + } + + res.status(200).send(newInfo); + } catch (error) { + console.log(error); + res + .status(500) + .json({ message: "Unable to save orbit build monitoring details" }); + } + } +); + +// EVERYTHING ABOVE THIS WORKS WITH APP ID VALIDATION + +// Pause or start monitoring +router.put( + "/togglestatus/:id", + [param("id").isUUID(4).withMessage("Invalid orbit monitoring Id")], + async (req, res, next) => { + try { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { id } = req.params; + const monitoring = await orbitMonitoring.findOne({ + where: { id }, + raw: true, + }); + const { isActive } = monitoring; + + // flipping isActive + await orbitMonitoring.update( + { isActive: !isActive }, + { where: { id: id } } + ); + + // If isActive, it is in bre - remove from bree + if (isActive) { + await jobScheduler.removeJobFromScheduler(`Orbit Monitoring - ${id}`); + } + + const name = monitoring.name; + const cron = monitoring.cron; + + // If isActive = false, add it to bre + if (!isActive) { + await jobScheduler.createOrbitMonitoringJob({ + orbitMonitoring_id: id, + cron: cron, + }); + } + + res.status(200).send("Update successful"); + } catch (err) { + console.log(err); + } + } +); + +//delete +router.delete( + "/delete/:id/:name", + [param("id").isUUID(4).withMessage("Invalid orbit monitoring id")], + async (req, res, next) => { + try { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { id, name } = req.params; + const response = await orbitMonitoring.destroy({ + where: { id }, + }); + res.status(200).json({ message: `Deleted ${response} orbit monitoring` }); + + //Check if this job is in bree - if so - remove + const breeJobs = jobScheduler.getAllJobs(); + const expectedJobName = `Orbit Monitoring - ${id}`; + if (breeJobs?.length) { + for (job of breeJobs) { + if (job.name === expectedJobName) { + jobScheduler.removeJobFromScheduler(expectedJobName); + break; + } + } + } + } catch (err) { + res.status(500).json({ message: err.message }); + } + } +); + +router.get( + "/getOne/:application_id/:id", + [param("id").isUUID(4).withMessage("Invalid orbit id")], + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + + const { id, application_id } = req.params; + + const result = await orbitMonitoring.findOne({ + where: { id, application_id }, + raw: true, + }); + + res.status(200).send(result); + } catch (err) { + res.status(500).json({ message: err.message }); + console.error(err); + } + } +); + +router.get( + "/getWorkunits/:application_id", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + + const { application_id } = req.params; + + const result = await orbitMonitoring.findAll({ + where: { application_id }, + raw: true, + }); + + //connect to db + + let wuList = []; + if (result?.length) { + await Promise.all( + result.map(async (build) => { + let wu = await orbitBuilds.findAll({ + where: { application_id, name: build.build }, + raw: true, + }); + + if (wu.length > 0) { + wu.map((wu) => { + wuList.push(wu); + }); + } + + Promise.resolve; + }) + ); + } + //return finished list; + res.status(200).send(wuList); + } catch (err) { + res.status(500).json({ message: err.message }); + console.error(err); + } + } +); + +//refresh data, grab new builds +router.post( + "/updateList/:application_id", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const { application_id } = req.params; + if (!application_id) throw Error("Invalid app ID"); + + const query = + "select TOP 10 * from DimBuildInstance where SubStatus_Code = 'MEGAPHONE' order by DateUpdated desc"; + + const result = runSQLQuery(query, dbConfig); + + const sentNotifications = []; + //just grab the rows from result + let rows = result?.recordset; + + if (rows?.length) { + //loop through rows to build notifications and import + await Promise.all( + rows?.map(async (build) => { + //check if the build already exists + let orbitBuild = await orbitBuilds.findOne({ + where: { + build_id: build.BuildInstanceIdKey, + application_id: application_id, + }, + raw: true, + }); + + //if it doesn't exist, create it and send a notification + if (!orbitBuild) { + //create build + const newBuild = await orbitBuilds.create({ + application_id: application_id, + build_id: build.BuildInstanceIdKey, + name: build.Name, + metaData: { + lastRun: build.DateUpdated, + status: build.Status_Code, + subStatus: build.SubStatus_Code, + workunit: build.HpccWorkUnit, + EnvironmentName: build.EnvironmentName, + Template: build.BuildTemplate_Name, + }, + }); + + //if megaphone, send notification + // if (build.SubStatus_Code === "MEGAPHONE") + + //build and send email notification + if (integration.metaData.notificationEmails) { + let buildDetails = { + name: newBuild.name, + status: newBuild.metaData.status, + subStatus: newBuild.metaData.subStatus, + lastRun: newBuild.metaData.lastRun, + workunit: newBuild.metaData.workunit, + }; + + const emailBody = + notificationTemplate.orbitBuildEmailBody(buildDetails); + const emailRecipients = integration.metaData.notificationEmails; + + const notificationResponse = await notify({ + to: emailRecipients, + from: process.env.EMAIL_SENDER, + subject: + "Alert: Megaphone Substatus detected on Orbit Build " + + build.Name, + text: emailBody, + html: emailBody, + }); + + let notification_id = uuidv4(); + + sentNotifications.push({ + id: notification_id, + status: "notified", + notifiedTo: emailRecipients, + notification_channel: "eMail", + application_id, + notification_reason: "Megaphone Substatus", + monitoring_id: newBuild.id, + monitoring_type: "orbit", + }); + } + + // //build and send Teams notification + if (integration.metaData.notificationWebhooks) { + let facts = [ + { name: newBuild.name }, + { status: newBuild.metaData.status }, + { subStatus: newBuild.metaData.subStatus }, + { lastRun: newBuild.metaData.lastRun }, + { workunit: newBuild.metaData.workunit }, + ]; + let title = "Orbit Build Detectd With Megaphone Status"; + notification_id = uuidv4(); + const cardBody = notificationTemplate.orbitBuildMessageCard( + title, + facts, + notification_id + ); + await axios.post( + integration.metaData.notificationWebhooks, + cardBody + ); + + sentNotifications.push({ + id: notification_id, + status: "notified", + notifiedTo: emailRecipients, + notification_channel: "msTeams", + application_id, + notification_reason: "Megaphone Substatus", + monitoring_id: newBuild.id, + monitoring_type: "orbit", + }); + } + } + + return true; + }) + ); + } + + // Record notifications + if (sentNotifications.length > 0) { + monitoring_notifications.bulkCreate(sentNotifications); + } + + res.status(200).send(result); + } catch (err) { + // ... error checks + + console.log(err); + res + .status(400) + .send("There was an issue contacting the orbit reports server"); + } + } +); + +router.get( + "/getDomains/:application_id", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const query = + "select business_unit from pbi.dim_asr_domain_v where business_unit != 'Unassigned' AND business_unit != 'Not Applicable' order by business_unit asc"; + const result = await runSQLQuery(query, fidoDbConfig); + + if (result?.recordset) { + res.status(200).send(result.recordset); + } else { + throw Error("No domains found on Fido Server: " + query); + } + } catch (err) { + console.log(err); + res + .status(400) + .send("There was an issue contacting the orbit reports server"); + } + } +); + +router.get( + "/getProducts/:application_id", + [param("application_id").isUUID(4).withMessage("Invalid application id")], + async (req, res) => { + const errors = validationResult(req).formatWith( + validatorUtil.errorFormatter + ); + try { + if (!errors.isEmpty()) + return res.status(422).json({ success: false, errors: errors.array() }); + const query = + "select product_name from pbi.dim_asr_product_v order by product_name asc"; + const result = await runSQLQuery(query, fidoDbConfig); + + if (result?.recordset) { + res.status(200).send(result.recordset); + } else { + throw Error("No products found on Fido Server: " + query); + } + } catch (err) { + console.log(err); + res + .status(400) + .send("There was an issue contacting the orbit reports server"); + } + } +); +module.exports = router; diff --git a/server/server.js b/server/server.js index c8ca9802e..06dc98444 100644 --- a/server/server.js +++ b/server/server.js @@ -77,6 +77,8 @@ const api = require("./routes/api/read"); const jobmonitoring = require("./routes/jobmonitoring/read"); const superfileMonitoring = require("./routes/superfilemonitoring/read"); const cluster = require("./routes/cluster/read"); +const orbit = require("./routes/orbit/read"); +const integrations = require("./routes/integrations/read"); const teamsHook = require("./routes/msTeamsHook/read"); const notification_queue = require("./routes/notification_queue/read"); @@ -120,6 +122,8 @@ app.use("/api/clustermonitoring", clustermonitoring); app.use("/api/key", key); app.use("/api/jobmonitoring", jobmonitoring); app.use("/api/cluster", cluster); +app.use("/api/orbit", orbit); +app.use("/api/integrations", integrations); app.use("/api/teamsHook", teamsHook); app.use("/api/notification_queue", notification_queue); diff --git a/server/tempFiles/Tombolo-Notifications.CSV b/server/tempFiles/Tombolo-Notifications.CSV deleted file mode 100644 index 045f13370..000000000 --- a/server/tempFiles/Tombolo-Notifications.CSV +++ /dev/null @@ -1 +0,0 @@ -id,monitoringId,Channel,Reason,Status,Created,Deleted \ No newline at end of file diff --git a/server/tempFiles/Tombolo-Notifications.JSON b/server/tempFiles/Tombolo-Notifications.JSON deleted file mode 100644 index 0637a088a..000000000 --- a/server/tempFiles/Tombolo-Notifications.JSON +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/server/tempFiles/Tombolo-clusterUsage.CSV b/server/tempFiles/Tombolo-clusterUsage.CSV deleted file mode 100644 index 08ed77c3a..000000000 --- a/server/tempFiles/Tombolo-clusterUsage.CSV +++ /dev/null @@ -1,34 +0,0 @@ -type,date,maxUsage,meanUsage -thor,Mon Jun 19 2023 16:18:46 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Tue Jun 13 2023 16:15:00 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Mon Jun 12 2023 23:06:56 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Mon Jun 12 2023 19:06:56 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Mon Jun 12 2023 15:06:55 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Fri Jun 09 2023 20:53:34 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Fri Jun 09 2023 16:53:33 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Fri Jun 09 2023 12:53:33 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Thu Jun 08 2023 17:39:58 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Wed Jun 07 2023 19:50:27 GMT-0400 (Eastern Daylight Time),32,20.65 -thor,Wed Jun 07 2023 15:50:26 GMT-0400 (Eastern Daylight Time),32,20.65 -hthor,Mon Jun 19 2023 16:18:46 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Tue Jun 13 2023 16:15:00 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Mon Jun 12 2023 23:06:56 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Mon Jun 12 2023 19:06:56 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Mon Jun 12 2023 15:06:55 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Fri Jun 09 2023 20:53:34 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Fri Jun 09 2023 16:53:33 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Fri Jun 09 2023 12:53:33 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Thu Jun 08 2023 17:39:58 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Wed Jun 07 2023 19:50:27 GMT-0400 (Eastern Daylight Time),18,18 -hthor,Wed Jun 07 2023 15:50:26 GMT-0400 (Eastern Daylight Time),18,18 -roxie,Mon Jun 19 2023 16:18:46 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Tue Jun 13 2023 16:15:00 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Mon Jun 12 2023 23:06:56 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Mon Jun 12 2023 19:06:56 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Mon Jun 12 2023 15:06:55 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Fri Jun 09 2023 20:53:34 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Fri Jun 09 2023 16:53:33 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Fri Jun 09 2023 12:53:33 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Thu Jun 08 2023 17:39:58 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Wed Jun 07 2023 19:50:27 GMT-0400 (Eastern Daylight Time),18,13 -roxie,Wed Jun 07 2023 15:50:26 GMT-0400 (Eastern Daylight Time),18,13 \ No newline at end of file diff --git a/server/tempFiles/Tombolo-clusterUsage.JSON b/server/tempFiles/Tombolo-clusterUsage.JSON deleted file mode 100644 index c9a7d181b..000000000 --- a/server/tempFiles/Tombolo-clusterUsage.JSON +++ /dev/null @@ -1 +0,0 @@ -[{"thor":[{"date":"2023-06-19T20:18:46.324Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-13T20:15:00.246Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-13T03:06:56.044Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-12T23:06:56.047Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-12T19:06:55.764Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-10T00:53:34.213Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-09T20:53:33.757Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-09T16:53:33.854Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-08T21:39:58.257Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-07T23:50:27.030Z","maxUsage":32,"meanUsage":20.65},{"date":"2023-06-07T19:50:26.108Z","maxUsage":32,"meanUsage":20.65}],"hthor":[{"date":"2023-06-19T20:18:46.324Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-13T20:15:00.246Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-13T03:06:56.044Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-12T23:06:56.047Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-12T19:06:55.764Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-10T00:53:34.213Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-09T20:53:33.757Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-09T16:53:33.854Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-08T21:39:58.257Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-07T23:50:27.030Z","maxUsage":18,"meanUsage":18},{"date":"2023-06-07T19:50:26.108Z","maxUsage":18,"meanUsage":18}],"roxie":[{"date":"2023-06-19T20:18:46.324Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-13T20:15:00.246Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-13T03:06:56.044Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-12T23:06:56.047Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-12T19:06:55.764Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-10T00:53:34.213Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-09T20:53:33.757Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-09T16:53:33.854Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-08T21:39:58.257Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-07T23:50:27.030Z","maxUsage":18,"meanUsage":13},{"date":"2023-06-07T19:50:26.108Z","maxUsage":18,"meanUsage":13}]}] \ No newline at end of file diff --git a/server/tests/mock-data/OrbitMonitoringBad.json b/server/tests/mock-data/OrbitMonitoringBad.json new file mode 100644 index 000000000..d3f54cf5e --- /dev/null +++ b/server/tests/mock-data/OrbitMonitoringBad.json @@ -0,0 +1,32 @@ +{ + "name": "test12342134", + "cron": "*/5 * * * *", + "build": "DL V1 Build", + "application_id": "badid", + "businessUnit": "Insurance", + "product": "DL", + "host": "test.test.com", + "severityCode": 2, + "notifyCondition": ["buildStatus"], + "buildStatus": ["discarded", "build_available_for_use"], + "isActive": 1, + "primaryContact": "test@test.com", + "secondaryContact": "test2@test.com", + "notificationChannels": ["eMail"], + "metaData": { + "lastMonitored": 1706544111312, + "monitoringCondition": { + "notifyCondition": ["buildStatus"], + "buildStatus": ["discarded", "build_available_for_use"] + }, + "notifications": [ + { + "channel": "eMail" + } + ], + "isActive": 1 + }, + "dataValues": { + "id": "11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000" + } +} diff --git a/server/tests/mock-data/cluster.json b/server/tests/mock-data/cluster.json index 4f8b14cd0..be9904b86 100644 --- a/server/tests/mock-data/cluster.json +++ b/server/tests/mock-data/cluster.json @@ -1,4 +1,5 @@ { + "id": "625be1ec-6387-4802-93fc-0ae52f0ebde7", "thor_host": "thor", "thor_port": 1800, "username": "", diff --git a/server/tests/mock-data/global.json b/server/tests/mock-data/global.json new file mode 100644 index 000000000..dd4c80261 --- /dev/null +++ b/server/tests/mock-data/global.json @@ -0,0 +1,8 @@ +{ + "application_id": "11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000", + "badApplicationId": "23123nbjkk123", + "clusterId": "11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000", + "name": "test", + "keyword": "test2", + "id": "11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000" +} diff --git a/server/tests/mock-data/integration.json b/server/tests/mock-data/integration.json new file mode 100644 index 000000000..9596e1f40 --- /dev/null +++ b/server/tests/mock-data/integration.json @@ -0,0 +1,4 @@ +{ + "active": true, + "id": "625be1ec-6387-4802-93fc-0ae52f0ebde7" +} diff --git a/server/tests/mock-data/orbitMonitoring.json b/server/tests/mock-data/orbitMonitoring.json new file mode 100644 index 000000000..edf2e73d7 --- /dev/null +++ b/server/tests/mock-data/orbitMonitoring.json @@ -0,0 +1,32 @@ +{ + "name": "test12342134", + "cron": "*/5 * * * *", + "build": "DL V1 Build", + "application_id": "11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000", + "businessUnit": "Insurance", + "product": "DL", + "host": "test.test.com", + "severityCode": 2, + "notifyCondition": ["buildStatus"], + "buildStatus": ["discarded", "build_available_for_use"], + "isActive": 1, + "primaryContact": "test@test.com", + "secondaryContact": "test2@test.com", + "notificationChannels": ["eMail"], + "metaData": { + "lastMonitored": 1706544111312, + "monitoringCondition": { + "notifyCondition": ["buildStatus"], + "buildStatus": ["discarded", "build_available_for_use"] + }, + "notifications": [ + { + "channel": "eMail" + } + ], + "isActive": 1 + }, + "dataValues": { + "id": "11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000" + } +} diff --git a/server/tests/mock-data/sqlData.json b/server/tests/mock-data/sqlData.json new file mode 100644 index 000000000..e9554c4bc --- /dev/null +++ b/server/tests/mock-data/sqlData.json @@ -0,0 +1,16 @@ +{ + "recordset": [ + { + "WorkUnit": "W20231229-030004-2", + "Date": "2023-12-29 12:50:46.4686170", + "Status": "QA_IN_PROGRESS", + "Name": "Test 1" + }, + { + "WorkUnit": "W20231229-030004-2", + "Date": "2023-12-29 12:50:46.4686170", + "Status": "QA_IN_PROGRESS", + "Name": "Test 2" + } + ] +} diff --git a/server/tests/unit/integration.test.js b/server/tests/unit/integration.test.js new file mode 100644 index 000000000..5835e8ebb --- /dev/null +++ b/server/tests/unit/integration.test.js @@ -0,0 +1,86 @@ +//dependencies to start app.js and make calls +const express = require("express"); +const request = require("supertest"); +const app = express(); +app.use(express.json()); + +//import model and spy functions that will be called +const models = require("../../models"); +const integrations = models.integrations; +integrations.findOne = jest.fn(() => { + return mockIntegrationData; +}); +integrations.findAll = jest.fn(); +integrations.update = jest.fn(); + +//bring in standard testing data +const mockData = require("../mock-data/global.json"); +const mockIntegrationData = require("../mock-data/integration.json"); + +//route and models imported for testing +const integration = require("../../routes/integrations/read"); +app.use("/api/integration", integration); + +//write tests +describe("Integration Tests", () => { + //globals needed for multiple tests + const { application_id, badApplicationId, name } = mockData; + let response; + + beforeEach(() => { + jest.resetModules(); // Most important - it clears the cache + response = null; + }); + + describe("Route Checks", () => { + test("Get all", async () => { + response = await request(app).get( + `/api/integration/get/${application_id}` + ); + expect(integrations.findAll).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Get all - Bad App ID", async () => { + response = await request(app).get( + `/api/integration/get/${badApplicationId}` + ); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("toggle active", async () => { + response = await request(app).put( + `/api/integration/toggle/${application_id}/${name}` + ); + expect(response.status).toBe(200); + expect(integrations.findOne).toHaveBeenCalledTimes(1); + expect(integrations.update).toHaveBeenCalledTimes(1); + }); + + test("toggle active - Bad App ID", async () => { + response = await request(app).put( + `/api/integration/toggle/${badApplicationId}/${name}` + ); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("update ", async () => { + response = await request(app).put( + `/api/integration/update/${application_id}/${name}` + ); + expect(integrations.findOne).toHaveBeenCalledTimes(1); + expect(integrations.update).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("update - Bad App ID", async () => { + response = await request(app).put( + `/api/integration/update/${badApplicationId}/${name}` + ); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + }); +}); diff --git a/server/tests/unit/notification.test.js b/server/tests/unit/notification.test.js index cc9debea0..08380fa8b 100644 --- a/server/tests/unit/notification.test.js +++ b/server/tests/unit/notification.test.js @@ -33,13 +33,13 @@ describe("Notifications Tests", () => { expect(response.status).toBe(200); }); - test("Get all - Bad Application ID", async () => { - response = await request(app).get( - `/api/notifications/read/badapplicationid` - ); - expect(response.status).toBe(422); - expect(response.body.success).toBe(false); - }); + // test("Get all - Bad Application ID", async () => { + // response = await request(app).get( + // `/api/notifications/read/badapplicationid` + // ); + // expect(response.status).toBe(422); + // expect(response.body.success).toBe(false); + // }); test("Get CSV File", async () => { response = await request(app).get( diff --git a/server/tests/unit/orbit.test.js b/server/tests/unit/orbit.test.js new file mode 100644 index 000000000..d8c7f504c --- /dev/null +++ b/server/tests/unit/orbit.test.js @@ -0,0 +1,279 @@ +//dependencies to start app.js and make calls +const express = require("express"); +const request = require("supertest"); +const app = express(); +app.use(express.json()); +const sql = require("mssql"); + +//bring in standard testing data +const mockData = require("../mock-data/global.json"); +const mockOrbitMonitoringData = require("../mock-data/orbitMonitoring.json"); +const mockOrbitMonitoringDataBadID = require("../mock-data/orbitMonitoringBad.json"); +const mockSqlReturnData = require("../mock-data/sqlData.json"); + +//import model and spy functions that will be called +const models = require("../../models"); +const jobScheduler = require("../../job-scheduler"); +const orbitBuild = models.orbitBuilds; +const orbitMonitoring = models.orbitMonitoring; +const monitoring_notifications = models.monitoring_notifications; + +//orbit Builds mocks +orbitBuild.findOne = jest.fn(); +orbitBuild.findAll = jest.fn(); +orbitBuild.update = jest.fn(); +orbitBuild.create = jest.fn(); +orbitBuild.destroy = jest.fn(); + +//orbit Monitoring mocks +orbitMonitoring.findOne = jest.fn(() => { + return mockOrbitMonitoringData; +}); +orbitMonitoring.findAll = jest.fn(() => { + return mockOrbitMonitoringData; +}); +orbitMonitoring.update = jest.fn(); +orbitMonitoring.create = jest.fn(() => { + return mockOrbitMonitoringData; +}); +orbitMonitoring.destroy = jest.fn(); + +//monitoring notifications mocks +monitoring_notifications.bulkCreate = jest.fn(); + +//job scheduler mocks +jobScheduler.createOrbitMonitoringJob = jest.fn(); +jobScheduler.removeJobFromScheduler = jest.fn(); +jobScheduler.getAllJobs = jest.fn(); + +//sql mocks +sql.connect = jest.fn(); +sql.query = jest.fn(() => { + return mockSqlReturnData; +}); + +//route and models imported for testing +const orbit = require("../../routes/orbit/read"); +const orbitbuilds = require("../../models/orbitbuilds"); +app.use("/api/orbit", orbit); + +describe("Integration Tests", () => { + //globals needed for multiple tests + const { application_id, badApplicationId, name, keyword, id } = mockData; + let response; + + beforeEach(() => { + jest.resetModules(); // Most important - it clears the cache + response = null; + }); + + describe("Route Checks", () => { + test("Create One", async () => { + response = await request(app) + .post(`/api/orbit/`) + .send(mockOrbitMonitoringData) + .set("Content-Type", "application/json") + .set("Accept", "application/json"); + + expect(orbitMonitoring.create).toHaveBeenCalledTimes(1); + expect(sql.query).toHaveBeenCalledTimes(1); + expect(response.status).toBe(201); + }); + + test("Create One - Bad App ID", async () => { + response = await request(app) + .post(`/api/orbit/`) + .send(mockOrbitMonitoringDataBadID) + .set("Content-Type", "application/json") + .set("Accept", "application/json"); + + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Get All Monitorings", async () => { + response = await request(app).get( + `/api/orbit/allMonitorings/${application_id}` + ); + expect(orbitMonitoring.findAll).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Get All Monitorings - Bad Data", async () => { + response = await request(app).get( + `/api/orbit/allMonitorings/${badApplicationId}` + ); + expect(orbitMonitoring.findAll).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Get All Builds", async () => { + response = await request(app).get( + `/api/orbit/allMonitorings/${application_id}` + ); + expect(orbitMonitoring.findAll).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Get All Builds - Bad Data", async () => { + response = await request(app).get( + `/api/orbit/allMonitorings/${badApplicationId}` + ); + expect(orbitMonitoring.findAll).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Get Searched Results", async () => { + response = await request(app).get( + `/api/orbit/search/${application_id}/${keyword}` + ); + expect(sql.query).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Get Searched Results - Bad Data", async () => { + response = await request(app).get( + `/api/orbit/search/${badApplicationId}/${keyword}` + ); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Get Build Details from SQL", async () => { + response = await request(app).get( + `/api/orbit/getOrbitBuildDetails/${name}` + ); + expect(sql.query).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Get Build Details from SQL", async () => { + response = await request(app).get( + `/api/orbit/getOrbitBuildDetails/${"&"}` + ); + expect(sql.query).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Update Monitoring", async () => { + response = await request(app) + .put(`/api/orbit/`) + .send(mockOrbitMonitoringData) + .set("Content-Type", "application/json") + .set("Accept", "application/json"); + + expect(orbitMonitoring.findOne).toHaveBeenCalledTimes(1); + expect(orbitMonitoring.update).toHaveBeenCalledTimes(1); + expect(sql.connect).toHaveBeenCalledTimes(1); + expect(sql.query).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Update Monitoring", async () => { + response = await request(app) + .put(`/api/orbit/`) + .send(mockOrbitMonitoringDataBadID) + .set("Content-Type", "application/json") + .set("Accept", "application/json"); + + expect(orbitMonitoring.findOne).toHaveBeenCalledTimes(0); + expect(orbitMonitoring.update).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("toggle status", async () => { + response = await request(app).put( + `/api/orbit/togglestatus/${application_id}` + ); + expect(orbitMonitoring.findOne).toHaveBeenCalledTimes(1); + expect(orbitMonitoring.update).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("toggle status - bad id", async () => { + response = await request(app).put( + `/api/orbit/togglestatus/${badApplicationId}` + ); + expect(orbitMonitoring.findOne).toHaveBeenCalledTimes(0); + expect(orbitMonitoring.update).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Delete", async () => { + response = await request(app).delete( + `/api/orbit/delete/${application_id}/${name}` + ); + expect(orbitMonitoring.destroy).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Delete", async () => { + response = await request(app).delete( + `/api/orbit/delete/${badApplicationId}/${name}` + ); + expect(orbitMonitoring.destroy).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Get one", async () => { + response = await request(app).get( + `/api/orbit/getOne/${application_id}/${id}` + ); + expect(orbitMonitoring.findOne).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Get one - Bad ID", async () => { + response = await request(app).get( + `/api/orbit/getOne/${badApplicationId}/${id}` + ); + expect(orbitMonitoring.findOne).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Get WorkUnits", async () => { + response = await request(app).get( + `/api/orbit/getWorkunits/${application_id}` + ); + expect(orbitMonitoring.findAll).toHaveBeenCalledTimes(1); + expect(response.status).toBe(200); + }); + + test("Get WorkUnits - Bad ID", async () => { + response = await request(app).get( + `/api/orbit/getWorkunits/${badApplicationId}` + ); + expect(orbitMonitoring.findAll).toHaveBeenCalledTimes(0); + expect(orbitBuild.findAll).toHaveBeenCalledTimes(0); + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + + test("Update List", async () => { + response = await request(app).post( + `/api/orbit/updateList/${application_id}` + ); + + expect(sql.connect).toHaveBeenCalledTimes(1); + expect(sql.query).toHaveBeenCalledTimes(1); + + expect(response.status).toBe(200); + }); + + test("Update List - Bad ID", async () => { + response = await request(app).post( + `/api/orbit/updateList/${badApplicationId}` + ); + + expect(response.status).toBe(422); + expect(response.body.success).toBe(false); + }); + }); +});