diff options
Diffstat (limited to 'sdnr/wt/odlux/framework')
-rw-r--r-- | sdnr/wt/odlux/framework/package.json | 12 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/pom.xml | 4 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/app.tsx | 8 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/material-table/index.tsx | 44 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx | 4 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/components/material-table/utilities.ts | 64 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts | 2 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/services/notificationService.ts | 156 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/services/restService.ts | 42 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/src/views/about.tsx | 67 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/tsconfig.json | 2 | ||||
-rw-r--r-- | sdnr/wt/odlux/framework/webpack.config.js | 24 |
12 files changed, 282 insertions, 147 deletions
diff --git a/sdnr/wt/odlux/framework/package.json b/sdnr/wt/odlux/framework/package.json index 3ecd34c23..345fc8b4c 100644 --- a/sdnr/wt/odlux/framework/package.json +++ b/sdnr/wt/odlux/framework/package.json @@ -25,18 +25,18 @@ "license": "Apache-2.0",
"peerDependencies": {
"@types/node": "11.11.6",
- "@types/react": "16.9.19",
- "@types/react-dom": "16.9.5",
- "@types/react-router-dom": "4.3.1",
+ "@types/react": "17.0.3",
+ "@types/react-dom": "17.0.2",
+ "@types/react-router-dom": "5.1.7",
"@material-ui/core": "4.11.0",
"@material-ui/icons": "4.9.1",
"@types/classnames": "2.2.6",
"@types/flux": "3.1.8",
"@types/jquery": "3.3.10",
"jquery": "3.3.1",
- "react": "16.12.0",
- "react-dom": "16.12.0",
- "react-router-dom": "4.3.1",
+ "react": "17.0.1",
+ "react-dom": "17.0.1",
+ "react-router-dom": "5.2.0",
"@fortawesome/react-fontawesome": "0.1.3",
"@fortawesome/fontawesome-svg-core": "1.2.12",
"@fortawesome/free-solid-svg-icons": "5.6.3",
diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml index 3f9ecbee6..9d648b52d 100644 --- a/sdnr/wt/odlux/framework/pom.xml +++ b/sdnr/wt/odlux/framework/pom.xml @@ -46,7 +46,7 @@ <properties> <buildtime>${maven.build.timestamp}</buildtime> <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion> - <buildno>90.49cc396(21/02/17)</buildno> + <buildno>96.078ad12(21/03/25)</buildno> <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version> </properties> @@ -126,7 +126,7 @@ </goals> <phase>initialize</phase> <configuration> - <arguments>add lerna@3.13.1 -W --exact</arguments> + <arguments>add lerna@3.22.1 -W --exact</arguments> <installDirectory>${project.basedir}</installDirectory> <workingDirectory>${project.basedir}/../</workingDirectory> </configuration> diff --git a/sdnr/wt/odlux/framework/src/app.tsx b/sdnr/wt/odlux/framework/src/app.tsx index 23ae2fbc9..2d913be1b 100644 --- a/sdnr/wt/odlux/framework/src/app.tsx +++ b/sdnr/wt/odlux/framework/src/app.tsx @@ -68,6 +68,10 @@ export const runApplication = () => { const initialToken = localStorage.getItem("userToken");
const applicationStore = applicationStoreCreator();
+ if (initialToken) {
+ applicationStore.dispatch(new UpdateUser(User.fromString(initialToken) || undefined));
+ }
+
window.onerror = function (msg: string, url: string, line: number, col: number, error: Error) {
// Note that col & error are new to the HTML 5 spec and may not be
// supported in every browser. It worked for me in Chrome.
@@ -98,9 +102,7 @@ export const runApplication = () => { ReactDOM.render(<App />, document.getElementById('app'));
- if (initialToken) {
- applicationStore.dispatch(new UpdateUser(User.fromString(initialToken) || undefined));
- }
+
};
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx index 7d4633bc6..c74fd1a38 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx @@ -32,13 +32,14 @@ import { EnhancedTableHead } from './tableHead'; import { EnhancedTableFilter } from './tableFilter'; import { ColumnModel, ColumnType } from './columnModel'; -import { Omit, Menu } from '@material-ui/core'; +import { Omit, Menu, makeStyles } from '@material-ui/core'; import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon'; import { DividerTypeMap } from '@material-ui/core/Divider'; import { MenuItemProps } from '@material-ui/core/MenuItem'; import { flexbox } from '@material-ui/system'; +import { RowDisabled } from './utilities'; export { ColumnModel, ColumnType } from './columnModel'; type propType = string | number | null | undefined | (string | number)[]; @@ -103,6 +104,34 @@ const styles = (theme: Theme) => createStyles({ } }); +const useTableRowExtStyles = makeStyles((theme: Theme) => createStyles({ + disabled: { + color: "rgba(180, 180, 180, 0.7)", + }, +})); + +type GetStatelessComponentProps<T> = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any; +type TableRowExtProps = GetStatelessComponentProps<typeof TableRow> & { disabled: boolean }; +const TableRowExt : React.FC<TableRowExtProps> = (props) => { + const [disabled, setDisabled] = React.useState(true); + const classes = useTableRowExtStyles(); + + const onMouseDown = (ev: React.MouseEvent<HTMLElement>) => { + if (ev.button ===1){ + setDisabled(!disabled); + ev.preventDefault(); + ev.stopPropagation(); + } else if (props.disabled && disabled) { + ev.preventDefault(); + ev.stopPropagation(); + } + }; + + return ( + <TableRow {...{...props, color: props.disabled && disabled ? '#a0a0a0' : undefined , className: props.disabled && disabled ? classes.disabled : '', onMouseDown, onContextMenu: props.disabled && disabled ? onMouseDown : props.onContextMenu } } /> + ); +}; + export type MaterialTableComponentState<TData = {}> = { order: 'asc' | 'desc'; orderBy: string | null; @@ -130,7 +159,7 @@ type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles> & { enableSelection?: boolean; disableSorting?: boolean; disableFilter?: boolean; - customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void }[]; + customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void, disabled?: boolean }[]; onHandleClick?(event: React.MouseEvent<HTMLTableRowElement>, rowData: TData): void; createContextMenu?: (row: TData) => React.ReactElement<MenuItemProps | DividerTypeMap<{}, "hr">, React.ComponentType<MenuItemProps | DividerTypeMap<{}, "hr">>>[]; }; @@ -222,12 +251,12 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate <TableBody> {showFilter && <EnhancedTableFilter columns={columns} filter={filter} onFilterChanged={this.onFilterChanged} enableSelection={this.props.enableSelection} /> || null} {rows // may need ordering here - .map((entry: TData & { [key: string]: any }, index) => { + .map((entry: TData & { [RowDisabled]?: boolean, [kex: string]: any }, index) => { const entryId = getId(entry); const isSelected = this.isSelected(entryId); const contextMenu = (this.props.createContextMenu && this.state.contextMenuInfo.index === index && this.props.createContextMenu(entry)) || null; return ( - <TableRow + <TableRowExt hover onClick={event => { if (this.props.createContextMenu) { @@ -252,9 +281,10 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate tabIndex={-1} key={entryId} selected={isSelected} + disabled={entry[RowDisabled] || false} > {this.props.enableSelection - ? <TableCell padding="checkbox" style={{ width: "50px" }}> + ? <TableCell padding="checkbox" style={{ width: "50px", color: entry[RowDisabled] || false ? "inherit" : undefined } }> <Checkbox checked={isSelected} /> </TableCell> : null @@ -264,7 +294,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate col => { const style = col.width ? { width: col.width } : {}; return ( - <TableCell aria-label={col.title? col.title.toLowerCase().replace(/\s/g, "-") : col.property.toLowerCase().replace(/\s/g, "-")} key={col.property} align={col.type === ColumnType.numeric && !col.align ? "right" : col.align} style={style}> + <TableCell style={ entry[RowDisabled] || false ? { ...style, color: "inherit" } : style } aria-label={col.title? col.title.toLowerCase().replace(/\s/g, "-") : col.property.toLowerCase().replace(/\s/g, "-")} key={col.property} align={col.type === ColumnType.numeric && !col.align ? "right" : col.align} > {col.type === ColumnType.custom && col.customControl ? <col.customControl className={col.className} style={col.style} rowData={entry} /> : col.type === ColumnType.boolean @@ -280,7 +310,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate anchorPosition={this.state.contextMenuInfo.mouseY != null && this.state.contextMenuInfo.mouseX != null ? { top: this.state.contextMenuInfo.mouseY, left: this.state.contextMenuInfo.mouseX } : undefined}> {contextMenu} </Menu> || null} - </TableRow> + </TableRowExt> ); })} {emptyRows > 0 && ( diff --git a/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx b/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx index 3b2f8e0a8..f7de0a062 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/tableToolbar.tsx @@ -67,7 +67,7 @@ interface ITableToolbarComponentProps extends WithStyles<typeof styles> { numSelected: number | null; title?: string; tableId?: string; - customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void }[]; + customActionButtons?: { icon: React.ComponentType<SvgIconProps>, tooltip?: string, onClick: () => void, disabled?: boolean }[]; onToggleFilter: () => void; onExportToCsv: () => void; } @@ -110,7 +110,7 @@ class TableToolbarComponent extends React.Component<ITableToolbarComponentProps, {this.props.customActionButtons ? this.props.customActionButtons.map((action, ind) => ( <Tooltip key={`custom-action-${ind}`} title={action.tooltip || ''}> - <IconButton aria-label={buttonPrefix + `custom-action-${ind}`} onClick={() => action.onClick()}> + <IconButton disabled={action.disabled} aria-label={buttonPrefix + `custom-action-${ind}`} onClick={() => action.onClick()}> <action.icon /> </IconButton> </Tooltip> diff --git a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts index 07ffe2ff5..544e14e01 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts +++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts @@ -21,12 +21,14 @@ import { Dispatch } from '../../flux/store'; import { AddErrorInfoAction } from '../../actions/errorActions'; import { IApplicationStoreState } from '../../store/applicationStore'; +export const RowDisabled = Symbol("RowDisabled"); import { DataCallback } from "."; + export interface IExternalTableState<TData> { order: 'asc' | 'desc'; orderBy: string | null; selected: any[] | null; - rows: TData[]; + rows: (TData & { [RowDisabled]?: boolean })[]; total: number; page: number; rowsPerPage: number; @@ -36,8 +38,31 @@ export interface IExternalTableState<TData> { preFilter: { [property: string]: string }; } +export type ExternalMethodes<TData> = { + reloadAction: (dispatch: Dispatch, getAppState: () => IApplicationStoreState) => Promise<void | AddErrorInfoAction>; + createActions: (dispatch: Dispatch, skipRefresh?: boolean) => { + onRefresh: () => void; + onHandleRequestSort: (orderBy: string) => void; + onHandleExplicitRequestSort: (property: string, sortOrder: "asc" | "desc") => void; + onToggleFilter: (refresh?: boolean | undefined) => void; + onFilterChanged: (property: string, filterTerm: string) => void; + onHandleChangePage: (page: number) => void; + onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void; + }; + createPreActions: (dispatch: Dispatch, skipRefresh?: boolean) => { + onPreFilterChanged: (preFilter: { + [key: string]: string; + }) => void; + }; + createProperties: (state: IApplicationStoreState) => IExternalTableState<TData>; + actionHandler: IActionHandler<IExternalTableState<TData>, Action>; +} + + /** Create an actionHandler and actions for external table states. */ -export function createExternal<TData>(callback: DataCallback<TData>, selectState: (appState: IApplicationStoreState) => IExternalTableState<TData>) { +export function createExternal<TData>(callback: DataCallback<TData>, selectState: (appState: IApplicationStoreState) => IExternalTableState<TData>) : ExternalMethodes<TData> ; +export function createExternal<TData>(callback: DataCallback<TData>, selectState: (appState: IApplicationStoreState) => IExternalTableState<TData>, disableRow: (data: TData) => boolean) : ExternalMethodes<TData>; +export function createExternal<TData>(callback: DataCallback<TData>, selectState: (appState: IApplicationStoreState) => IExternalTableState<TData>, disableRow?: (data: TData) => boolean) : ExternalMethodes<TData> { //#region Actions abstract class TableAction extends Action { } @@ -131,7 +156,9 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState state = { ...state, loading: false, - rows: action.result.rows, + rows: disableRow + ? action.result.rows.map((row: TData) => ({...row, [RowDisabled]: disableRow(row) })) + : action.result.rows, total: action.result.total, page: action.result.page, } @@ -191,7 +218,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState dispatch(new RefreshAction()); const ownState = selectState(getAppState()); const filter = { ...ownState.preFilter, ...(ownState.showFilter && ownState.filter || {}) }; - Promise.resolve(callback(ownState.page, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)).then(result => { + return Promise.resolve(callback(ownState.page, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)).then(result => { if (ownState.page > 0 && ownState.rowsPerPage * ownState.page > result.total) { //if result is smaller than the currently shown page, new search and repaginate @@ -207,30 +234,7 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState } - }).catch(error => new AddErrorInfoAction(error)); - }; - - const reloadActionAsync = async (dispatch: Dispatch, getAppState: () => IApplicationStoreState) => { - dispatch(new RefreshAction()); - const ownState = selectState(getAppState()); - const filter = { ...ownState.preFilter, ...(ownState.showFilter && ownState.filter || {}) }; - - try { - const result = await Promise.resolve(callback(ownState.page, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)); - - - if (ownState.page > 0 && ownState.rowsPerPage * ownState.page > result.total) { //if result is smaller than the currently shown page, new search and repaginate - - let newPage = Math.floor(result.total / ownState.rowsPerPage); - - const repaginationResult = await Promise.resolve(callback(newPage, ownState.rowsPerPage, ownState.orderBy, ownState.order, filter)); - dispatch(new SetResultAction(repaginationResult)); - } else { - dispatch(new SetResultAction(result)); - } - } catch (error) { - new AddErrorInfoAction(error); - } + }).catch(error => dispatch(new AddErrorInfoAction(error))); }; const createPreActions = (dispatch: Dispatch, skipRefresh: boolean = false) => { @@ -303,6 +307,6 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState createProperties: createProperties, createPreActions: createPreActions, actionHandler: externalTableStateActionHandler, - reloadActionAsync: reloadActionAsync, } -}
\ No newline at end of file +} + diff --git a/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts b/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts index b5c1ee7b1..06df6709f 100644 --- a/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts +++ b/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts @@ -58,7 +58,7 @@ const applicationStateInit: IApplicationState = { export const configureApplication = (config: ApplicationConfig) => { applicationStateInit.authentication = config.authentication === "oauth" ? "oauth" : "basic"; - applicationStateInit.enablePolicy = config.authentication ? true : false; + applicationStateInit.enablePolicy = config.enablePolicy ? true : false; } export const applicationStateHandler: IActionHandler<IApplicationState> = (state = applicationStateInit, action) => { diff --git a/sdnr/wt/odlux/framework/src/services/notificationService.ts b/sdnr/wt/odlux/framework/src/services/notificationService.ts index 30091b574..5625b1f55 100644 --- a/sdnr/wt/odlux/framework/src/services/notificationService.ts +++ b/sdnr/wt/odlux/framework/src/services/notificationService.ts @@ -15,7 +15,6 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as X2JS from 'x2js'; import { ApplicationStore } from '../store/applicationStore'; import { SetWebsocketAction } from '../actions/websocketAction'; @@ -26,81 +25,95 @@ let userLoggedOut = false; let wasWebsocketConnectionEstablished: undefined | boolean; let applicationStore: ApplicationStore | null; - export interface IFormatedMessage { - notifType: string | null; - time: string; + "event-time": string, + "data": { + "counter": number, + "attribute-name": string, + "time-stamp": string, + "object-id-ref": string, + "new-value": string + }, + "node-id": string, + "type": { + "namespace": string, + "revision": string, + "type": string + } } export type SubscriptionCallback<TMessage extends IFormatedMessage = IFormatedMessage> = (msg: TMessage) => void; -function formatData(event: MessageEvent): IFormatedMessage | undefined { - - var x2js = new X2JS(); - var jsonObj: { [key: string]: IFormatedMessage } = x2js.xml2js(event.data); - if (jsonObj && typeof (jsonObj) === 'object') { - - const notifType = Object.keys(jsonObj)[0]; - const formated = jsonObj[notifType]; - formated.notifType = notifType; - formated.time = new Date().toISOString(); - return formated; - } - return undefined; - +function setCurrentSubscriptions(notificationSocket: WebSocket) { + const scopesToSubscribe = Object.keys(subscriptions); + if (notificationSocket.readyState === notificationSocket.OPEN) { + const data = { + 'data': 'scopes', + 'scopes':[{ + "schema":{ + "namespace":"*", + "revision":"*", + "notification": scopesToSubscribe + } + }] + }; + notificationSocket.send(JSON.stringify(data)); + return true; + }; + return false; } -export function subscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): boolean { +function addScope<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>) { const scopes = scope instanceof Array ? scope : [scope]; - // send all new scopes to subscribe - const newScopesToSubscribe: string[] = scopes.reduce((acc: string[], cur: string) => { - const currentCallbacks = subscriptions[cur]; - if (currentCallbacks) { - if (!currentCallbacks.some(c => c === callback)) { - currentCallbacks.push(callback); + // send all new scopes to subscribe + const newScopesToSubscribe: string[] = scopes.reduce((acc: string[], cur: string) => { + const currentCallbacks = subscriptions[cur]; + if (currentCallbacks) { + if (!currentCallbacks.some(c => c === callback)) { + currentCallbacks.push(callback); + } + } else { + subscriptions[cur] = [callback]; + acc.push(cur); } - } else { - subscriptions[cur] = [callback]; - acc.push(cur); - } - return acc; - }, []); + return acc; + }, []); - if (newScopesToSubscribe.length === 0) { - return true; - } + if (newScopesToSubscribe.length === 0) { + return true; + } + return false; +} - return true; +function removeScope<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>) { + const scopes = scope instanceof Array ? scope : [scope]; + scopes.forEach(s => { + const callbacks = subscriptions[s]; + const index = callbacks && callbacks.indexOf(callback); + if (index > -1) { + callbacks.splice(index, 1); + } + if (callbacks.length === 0) { + subscriptions[s] === undefined; + } + }); } +export function subscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> { + addScope(scope, callback) + return socketReady && socketReady.then((notificationSocket) => { + // send a subscription to all active scopes + return setCurrentSubscriptions(notificationSocket); + }) || true; +} export function unsubscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> { - return socketReady.then((notificationSocket) => { - const scopes = scope instanceof Array ? scope : [scope]; - scopes.forEach(s => { - const callbacks = subscriptions[s]; - const index = callbacks && callbacks.indexOf(callback); - if (index > -1) { - callbacks.splice(index, 1); - } - if (callbacks.length === 0) { - subscriptions[s] === undefined; - } - }); - + removeScope(scope, callback); + return socketReady && socketReady.then((notificationSocket) => { // send a subscription to all active scopes - const scopesToSubscribe = Object.keys(subscriptions); - if (notificationSocket.readyState === notificationSocket.OPEN) { - const data = { - 'data': 'scopes', - 'scopes': scopesToSubscribe - }; - notificationSocket.send(JSON.stringify(data)); - return true; - } - return false; - }); + return setCurrentSubscriptions(notificationSocket); + }) || true; } export const startNotificationService = (store: ApplicationStore) => { @@ -111,24 +124,24 @@ const connect = (): Promise<WebSocket> => { return new Promise((resolve, reject) => { const notificationSocket = new WebSocket(socketUrl); - notificationSocket.onmessage = (event) => { + notificationSocket.onmessage = (event: MessageEvent<string>) => { // process received event - if (typeof event.data === 'string') { - const formated = formatData(event); - if (formated && formated.notifType) { - const callbacks = subscriptions[formated.notifType]; + + if (event.data && typeof event.data === "string" ) { + const msg = JSON.parse(event.data) as IFormatedMessage; + const callbacks = msg?.type?.type && subscriptions[msg.type.type]; if (callbacks) { callbacks.forEach(cb => { // ensure all callbacks will be called try { - return cb(formated); + return cb(msg); } catch (reason) { console.error(reason); } }); } } - } + }; notificationSocket.onerror = function (error) { @@ -148,14 +161,7 @@ const connect = (): Promise<WebSocket> => { resolve(notificationSocket); // send a subscription to all active scopes - const scopesToSubscribe = Object.keys(subscriptions); - if (notificationSocket.readyState === notificationSocket.OPEN) { - const data = { - 'data': 'scopes', - 'scopes': scopesToSubscribe - }; - notificationSocket.send(JSON.stringify(data)); - }; + setCurrentSubscriptions(notificationSocket); }; notificationSocket.onclose = function (event) { @@ -171,8 +177,6 @@ const connect = (): Promise<WebSocket> => { } - - export const startWebsocketSession = () => { socketReady = connect(); userLoggedOut = false; diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts index f05c7b89f..c7b122449 100644 --- a/sdnr/wt/odlux/framework/src/services/restService.ts +++ b/sdnr/wt/odlux/framework/src/services/restService.ts @@ -15,6 +15,8 @@ * the License. * ============LICENSE_END========================================================================== */ + + import { ApplicationStore } from "../store/applicationStore"; import { ReplaceAction } from "../actions/navigationActions"; @@ -30,6 +32,46 @@ export const formEncode = (params: { [key: string]: string | number }) => Object return encodeURIComponent(key) + '=' + encodeURIComponent(params[key].toString()); }).join('&'); +const wildcardToRegexp = (pattern: string) => { + return new RegExp('^' + pattern.split(/\*\*/).map((p) => p.split(/\*+/).map((i) => i.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')).join('^[/]')).join('.*') + '$'); +}; + +export const getAccessPolicyByUrl = (url: string) => { + const result = { + GET : false, + POST: false, + PUT: false, + PATCH: false, + DELETE: false, + }; + + if (!applicationStore) return result; + + const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies }}} } = applicationStore!; + + result.GET = true; + result.POST = true; + result.PUT = true; + result.PATCH = true; + result.DELETE = true; + + if (!enablePolicy || !policies || policies.length === 0) return result; + + policies.forEach(p => { + const re = wildcardToRegexp(p.path); + if (re.test(url)) { + result.GET = p.methods.get != null ? p.methods.get : result.GET ; + result.POST = p.methods.post != null ? p.methods.post : result.POST ; + result.PUT = p.methods.put != null ? p.methods.put : result.PUT ; + result.PATCH = p.methods.patch != null ? p.methods.patch : result.PATCH ; + result.DELETE = p.methods.delete != null ? p.methods.delete : result.DELETE ; + } + }); + + return result; + +} + /** Sends a rest request to the given path. * @returns The data, or null it there was any error */ diff --git a/sdnr/wt/odlux/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx index f97d6ffb3..5d2257a3f 100644 --- a/sdnr/wt/odlux/framework/src/views/about.tsx +++ b/sdnr/wt/odlux/framework/src/views/about.tsx @@ -20,6 +20,7 @@ import * as marked from 'marked'; import * as hljs from 'highlight.js'; import { requestRestExt } from '../services/restService'; import { Button, Typography } from '@material-ui/core'; +import createBreakpoints from '@material-ui/core/styles/createBreakpoints'; const defaultRenderer = new marked.Renderer(); defaultRenderer.link = (href, title, text) => ( `<a target="_blank" rel="noopener noreferrer" href="${href}" title="${title}">${text}</a>` @@ -30,6 +31,23 @@ interface AboutState { isContentLoadedSucessfully: boolean; } +type odluxVersion= {version:string,build:string, framework: string, + applications:{ + configurationApp: string, + connectApp: string, + eventLogApp: string, + faultApp: string, + helpApp: string, + inventoryApp: string, + linkCalculationApp: string, + maintenanceApp: string, + mediatorApp: string, + networkMapApp: string, + permanceHistoryApp: string + }}; + +type topologyVersion = {version: string}; + class AboutComponent extends React.Component<any, AboutState> { textarea: React.RefObject<HTMLTextAreaElement>; @@ -40,23 +58,58 @@ class AboutComponent extends React.Component<any, AboutState> { this.textarea = React.createRef(); this.loadAboutContent(); } - private getMarkOdluxVersionMarkdownTable(data:{version:string,build:string}|null|undefined):string{ + + private getMarkOdluxVersionMarkdownTable(data:odluxVersion|null|undefined):string{ if(!data) { return ""; + }else{ + let applicationVersions= ''; + if(data.applications){ + + applicationVersions = `| Framework | ${data.framework}|\n `+ + `| ConnectApp | ${data.applications.connectApp}|\n `+ + `| FaultApp | ${data.applications.faultApp}|\n `+ + `| MaintenanceApp | ${data.applications.maintenanceApp}|\n `+ + `| ConfigurationApp | ${data.applications.configurationApp}|\n `+ + `| PerformanceHistoryApp | ${data.applications.permanceHistoryApp}|\n `+ + `| InventoryApp | ${data.applications.inventoryApp}|\n `+ + `| EventLogApp | ${data.applications.eventLogApp}|\n `+ + `| MediatorApp | ${data.applications.mediatorApp}|\n `+ + `| NetworkMapApp | ${data.applications.networkMapApp}|\n `+ + `| LinkCalculatorApp | ${data.applications.linkCalculationApp}|\n `+ + `| HelpApp | ${data.applications.helpApp}|\n `; + } + + return `| | |\n| --- | --- |\n| Version | ${data.version} |\n| Build timestamp | ${data.build}|\n`+ + applicationVersions; } - return `| | |\n| --- | --- |\n| Version | ${data.version} |\n| Build timestamp | ${data.build}|` } + + private getTopologyVersionMarkdownTable(data: topologyVersion|null|undefined){ + if(!data){ + return "No version"; + } + else + { + return `| | |\n| --- | --- |\n| Version | ${data.version} |\n` + } + } + private loadAboutContent(): void { const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1); const p1 = requestRestExt<string>('/about'); - const p2 = requestRestExt<{version:string,build:string}>(`${baseUri}version.json`); - Promise.all([p1,p2]).then((responses) => { + const p2 = requestRestExt<odluxVersion>(`${baseUri}version.json`); + const p3 = requestRestExt<any>(`/topology/info/version`); + + Promise.all([p1,p2, p3]).then((responses) => { const response = responses[0]; - const response2 = responses[1]; + const response2 = responses[1]; + const response3 = responses[2]; const content = response.status == 200 ? response.data : `${response.status} ${response.message}` || "Server error"; - const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? this.getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.status} ${response2.message}` || "ODLUX Server error"); + const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? this.getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error"); + const content3 = `\n## Topology API Version Info\n`+(response3.status == 200 ? this.getTopologyVersionMarkdownTable(response3.data): `Topology API not available`); const loadedSucessfully = response.status == 200 ? true : false; - this.setState({ content: (content + content2) || null, isContentLoadedSucessfully: loadedSucessfully }); + this.setState({ content: (content + content2 + content3 ) || null, isContentLoadedSucessfully: loadedSucessfully }); }).catch((error) => { this.setState({ content: error }) }) diff --git a/sdnr/wt/odlux/framework/tsconfig.json b/sdnr/wt/odlux/framework/tsconfig.json index d801f7c8e..cb36bc81a 100644 --- a/sdnr/wt/odlux/framework/tsconfig.json +++ b/sdnr/wt/odlux/framework/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/framework/webpack.config.js b/sdnr/wt/odlux/framework/webpack.config.js index 5d0d8fc9a..b5d31c262 100644 --- a/sdnr/wt/odlux/framework/webpack.config.js +++ b/sdnr/wt/odlux/framework/webpack.config.js @@ -201,55 +201,55 @@ module.exports = (env) => { proxy: {
"/about": {
// target: "http://10.20.6.29:48181",
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/yang-schema/": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/oauth/": {
// target: "https://10.20.35.188:30205",
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/oauth2/": {
// target: "https://10.20.35.188:30205",
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/database/": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/restconf/": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/rests/": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/help/": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/about/": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/tree/": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
secure: false
},
"/websocket": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
ws: true,
changeOrigin: true,
secure: false
},
"/apidoc": {
- target: "http://sdnr:8181",
+ target: "http://localhost:18181",
ws: true,
changeOrigin: true,
secure: false
|