Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import { EuiFlexGroup, EuiFlexItem, EuiSmallButton } from '@elastic/eui';
import React from 'react';
import { APP_PATH, FORECASTING_FEATURE_NAME } from '../../utils/constants';
import { useLocation } from 'react-router-dom';
import { constructHrefWithDataSourceId, getDataSourceFromURL } from '../../pages/utils/helpers';

export const CreateForecasterButtons = () => {
const location = useLocation();
const MDSQueryParams = getDataSourceFromURL(location);
const dataSourceId = MDSQueryParams.dataSourceId;

const createForecasterUrl = `${FORECASTING_FEATURE_NAME}#` + constructHrefWithDataSourceId(`${APP_PATH.CREATE_FORECASTER}`, dataSourceId, false);

return (
<EuiFlexGroup direction="row" gutterSize="m" justifyContent="center">
<EuiFlexItem grow={false}>
<EuiSmallButton
style={{ width: '200px' }}
fill
href={createForecasterUrl}
data-test-subj="createForecasterButton"
iconType="plusInCircle"
>
Create forecaster
</EuiSmallButton>
</EuiFlexItem>
</EuiFlexGroup>
);
};
5 changes: 3 additions & 2 deletions public/components/FormattedFormRow/FormattedFormRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type FormattedFormRowProps = {
title?: string;
formattedTitle?: ReactNode;
children: ReactElement;
hint?: string | string[];
hint?: string | string[] | ReactNode | ReactNode[];
isInvalid?: boolean;
error?: ReactNode | ReactNode[];
fullWidth?: boolean;
Expand All @@ -42,7 +42,8 @@ export const FormattedFormRow = (props: FormattedFormRowProps) => {
))
: null;

const { formattedTitle, linkToolTip, ...euiFormRowProps } = props;
// Extract hintLink to avoid passing it to EuiCompressedFormRow as an unknown prop
const { formattedTitle, hintLink, linkToolTip, ...euiFormRowProps } = props;

return (
<EuiCompressedFormRow
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import { EuiFlexGroup, EuiFlexItem, EuiSmallButton } from '@elastic/eui';
import React from 'react';
import { APP_PATH, FORECASTING_FEATURE_NAME } from '../../utils/constants';
import { useLocation } from 'react-router-dom';
import { constructHrefWithDataSourceId, getDataSourceFromURL } from '../../pages/utils/helpers';

export const RefreshForecastersButton = () => {
const location = useLocation();
const MDSQueryParams = getDataSourceFromURL(location);
const dataSourceId = MDSQueryParams.dataSourceId;

const refreshForecastersUrl = `${FORECASTING_FEATURE_NAME}#` + constructHrefWithDataSourceId(`${APP_PATH.LIST_FORECASTERS}`, dataSourceId, false);

return (
<EuiFlexGroup direction="row" gutterSize="m" justifyContent="center">
<EuiFlexItem grow={false}>
<EuiSmallButton
style={{ width: '200px' }}
href={refreshForecastersUrl}
data-test-subj="refreshForecastersButton"
iconType="refresh"
>
Refresh
</EuiSmallButton>
</EuiFlexItem>
</EuiFlexGroup>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import React from 'react';
import { render } from '@testing-library/react';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { RefreshForecastersButton } from '../RefreshForecastersButton';
import { getDataSourceEnabled, } from '../../../services';

// Mock the module
jest.mock('../../../services', () => ({
...jest.requireActual('../../../services'),
getDataSourceEnabled: jest.fn(),
}));

describe('<RefreshForecastersButton /> spec', () => {
beforeAll(() => {
Object.defineProperty(window, 'location', {
value: {
href: 'http://test.com',
pathname: '/',
search: '',
hash: '',
},
writable: true
});
});

beforeEach(() => {
// Mock the return value
(getDataSourceEnabled as jest.Mock).mockReturnValue({
enabled: false, // or true, depending on what you want to test
});
});

test('renders component', () => {
const history = createMemoryHistory();
const { container, getByText } = render(
<Router history={history}>
<RefreshForecastersButton />
</Router>
);

expect(container.firstChild).toMatchSnapshot();
expect(getByText('Refresh')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<RefreshForecastersButton /> spec renders component 1`] = `
<div
class="euiFlexGroup euiFlexGroup--gutterMedium euiFlexGroup--justifyContentCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
class="euiFlexItem euiFlexItem--flexGrowZero"
>
<a
class="euiButton euiButton--primary euiButton--small"
data-test-subj="refreshForecastersButton"
href="forecasting#/forecasters?"
rel="noreferrer"
style="width: 200px;"
>
<span
class="euiButtonContent euiButton__content"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon--inherit euiIcon-isLoading euiButtonContent__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.277 10.088c.02.014.04.03.057.047.582.55 1.134.812 1.666.812.586 0 1.84-.293 3.713-.88L9 6.212V2H7v4.212l-1.723 3.876Zm-.438.987L3.539 14h8.922l-1.32-2.969C9.096 11.677 7.733 12 7 12c-.74 0-1.463-.315-2.161-.925ZM6 2H5V1h6v1h-1v4l3.375 7.594A1 1 0 0 1 12.461 15H3.54a1 1 0 0 1-.914-1.406L6 6V2Z"
/>
</svg>
<span
class="euiButton__text"
>
Refresh
</span>
</span>
</a>
</div>
</div>
`;
53 changes: 53 additions & 0 deletions public/forecasting_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import { CoreStart, AppMountParameters } from '../../../src/core/public';
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route } from 'react-router-dom';
import { ForecastMain } from './pages/main/ForecastMain';
import { Provider } from 'react-redux';
import configureStore from './redux/configureStore';
import { CoreServicesContext } from './components/CoreServices/CoreServices';

export function renderApp(coreStart: CoreStart, params: AppMountParameters, landingPage: string | undefined, hideInAppSideNavBar: boolean) {
const http = coreStart.http;
const store = configureStore(http);

// Load Chart's dark mode CSS (if applicable)
const isDarkMode = coreStart.uiSettings.get('theme:darkMode') || false;
if (isDarkMode) {
require('@elastic/charts/dist/theme_only_dark.css');
} else {
require('@elastic/charts/dist/theme_only_light.css');
}

ReactDOM.render(
<Provider store={store}>
<Router>
<Route
render={(props) => (
<CoreServicesContext.Provider value={coreStart}>
<ForecastMain
setHeaderActionMenu={params.setHeaderActionMenu}
landingPage={landingPage}
hideInAppSideNavBar={hideInAppSideNavBar}
{...props}
/>
</CoreServicesContext.Provider>
)}
/>
</Router>
</Provider>,
params.element
);
return () => ReactDOM.unmountComponentAtNode(params.element);
}
80 changes: 79 additions & 1 deletion public/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import { InitProgress } from '../../server/models/interfaces';
import { DATA_TYPES } from '../utils/constants';
import { DETECTOR_STATE } from '../../server/utils/constants';
import { DETECTOR_STATE, FORECASTER_STATE } from '../../server/utils/constants';
import { Duration } from 'moment';
import moment from 'moment';
import { MDSQueryParams } from '../../server/models/types';
Expand Down Expand Up @@ -231,6 +231,56 @@ export type DetectorListItem = {
detectorType?: string;
};

export type Forecaster = {
primaryTerm: number;
seqNo: number;
id: string;
name: string;
description: string;
timeField: string;
indices: string[];
resultIndex?: string;
resultIndexMinAge?: number;
resultIndexMinSize?: number;
resultIndexTtl?: number;
flattenCustomResultIndex?: boolean;
filterQuery: { [key: string]: any };
featureAttributes: FeatureAttributes[];
windowDelay: { period: Schedule };
forecastInterval: { period: Schedule };
shingleSize: number;
uiMetadata: UiMetaData;
lastUpdateTime: number;
enabled?: boolean;
enabledTime?: number;
disabledTime?: number;
curState: FORECASTER_STATE; // combined state of realTime and runOnce
stateError: string; // combined error of realTime and runOnce
initProgress?: InitProgress; // realTime
categoryField?: string[];
taskId?: string; // runOnce
taskState?: FORECASTER_STATE; // runOnce
taskProgress?: number; // runOnce
taskError?: string; // runOnce
lastStateUpdateTime?: number;
imputationOption?: ImputationOption;
suggestedSeasonality?: number;
recencyEmphasis?: number;
horizon?: number;
history?: number;
lastUiBreakingChangeTime?: number;
};

export type ForecasterListItem = {
id: string;
name: string;
indices: string[];
curState: FORECASTER_STATE;
realTimeLastUpdateTime: number;
runOnceLastUpdateTime: number;
stateError: string;
};

export type EntityData = {
name: string;
value: string;
Expand Down Expand Up @@ -342,3 +392,31 @@ export interface MDSStates {
queryParams: MDSQueryParams;
selectedDataSourceId: string | undefined;
}

/* export type ForecastData = {
dataQuality: number;
forecasterId?: string;
endTime: number;
startTime: number;
plotTime?: number;
entity?: EntityData[];
features?: { [key: string]: ForecastFeatureAggregationData };
aggInterval?: string;
}; */

export type ForecastFeatureAggregationData = {
data: number;
name?: string;
endTime: number;
startTime: number;
plotTime?: number;
lowerBound: number[];
upperBound: number[];
forecastValue: number[];
forecastStartTime: number[];
forecastEndTime: number[];
};

/* export type ForecastResult = {
forecastResult: ForecastData[];
}; */
Loading
Loading