diff options
11 files changed, 262 insertions, 87 deletions
diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt-odlux/odlux/apps/configurationApp/src/actions/deviceActions.ts index d8ec4bfd9..615faaed7 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/actions/deviceActions.ts +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/actions/deviceActions.ts @@ -24,6 +24,7 @@ import { flattenViewElements, getReferencedDataList, resolveViewDescription, + createViewData, } from '../utilities/viewEngineHelper'; export class EnableValueSelector extends Action { @@ -62,6 +63,12 @@ export class UpdateOutputData extends Action { } } +export class UpdateNewData extends Action { + constructor(public newData: any) { + super(); + } +} + export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, _getState: () => IApplicationStoreState ) => { dispatch(new UpdateDeviceDescription('', {}, [])); @@ -138,6 +145,8 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: let currentNS: string | null = null; let defaultNS: string | null = null; + let newData: any = null; + dispatch(new SetCollectingSelectionData(true)); try { for (let ind = 0; ind < pathParts.length; ++ind) { @@ -150,32 +159,27 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`]; if (!viewElement) throw Error('Property [' + property + '] does not exist.'); - if (viewElement.isList && !key) { - if (pathParts.length - 1 > ind) { - dispatch(new SetCollectingSelectionData(false)); - throw new Error('No key for list [' + property + ']'); - } else if (vPath.endsWith('[]') && pathParts.length - 1 === ind) { + if (newData) { + // update view data + newData = newData[property]; - // empty key is used for new element - if (viewElement && 'viewId' in viewElement) viewSpecification = views[+viewElement.viewId]; - const data = Object.keys(viewSpecification.elements).reduce<{ [name: string]: any }>((acc, cur) => { - const elm = viewSpecification.elements[cur]; - if (elm.default) { - acc[elm.id] = elm.default || ''; - } - return acc; - }, {}); + } else if (viewElement.isList && !key) { + // handle new list element without key + if (pathParts[ind][1] === null) { - // create display specification - const ds: DisplaySpecification = { - displayMode: DisplayModeType.displayAsObject, - viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification), - keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined, - }; + // create new data if not already exists + newData = getState().configuration.viewDescription.newData; + if (!newData && viewElement && 'viewId' in viewElement) { + newData = createViewData(namespace, views[+viewElement.viewId], views); + dispatch(new UpdateNewData(newData)); + } - // update display specification - return dispatch(postProcessDisplaySpecificationActionCreator(vPath, data, ds)); + } else if ((pathParts.length) - 1 > ind) { + // handle list without key which is not a new element + dispatch(new SetCollectingSelectionData(false)); + throw new Error('No key for list [' + property + ']'); } + if (viewElement && isViewElementList(viewElement) && viewSpecification.parentView === '0') { // check if there is a reference as key const listSpecification = views[+viewElement.viewId]; @@ -232,8 +236,7 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: } extractList = true; } else { - // normal case & replaces unicode %2C if present - dataPath += `/${property}${key ? `=${key.replace(/\%2C/g, ',').replace(/\//ig, '%2F')}` : ''}`; + dataPath += `/${property}${key ? `=${key.replace(/\//ig, '%2F')}` : ''}`; // in case of the root element the required namespace will be added later, // while extracting the data @@ -248,7 +251,7 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: } else if (viewElement.uiType === 'rpc') { viewSpecification = views[+(viewElement.inputViewId || 0)]; - // create new instance & flaten + // create new instance & flatten inputViewSpecification = viewElement.inputViewId != null && { ...views[+(viewElement.inputViewId || 0)], elements: flattenViewElements(defaultNS, '', views[+(viewElement.inputViewId || 0)].elements, views, viewElement.label), @@ -261,11 +264,44 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: } } + if (newData) { + // create display specification + const ds: DisplaySpecification = { + displayMode: DisplayModeType.displayAsObject, + viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification), + keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined, + }; + + // update display specification + return dispatch(postProcessDisplaySpecificationActionCreator(vPath, newData, ds)); + } + let data: any = {}; // do not get any data from netconf if there is no view specified || this is the root element [0] || this is an rpc if (viewSpecification && !(viewSpecification.id === '0' || viewElement!.uiType === 'rpc')) { const restResult = (await restService.getConfigData(dataPath)); - if (!restResult.data) { + if (restResult.status === 409) { + // special case: if this is a list without any response + if (isViewElementList(viewElement!)) { + // create display specification + const ds: DisplaySpecification = { + displayMode: extractList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject, + viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification), + keyProperty: viewElement.key, + }; + // update display specification + return dispatch(postProcessDisplaySpecificationActionCreator(vPath, [], ds)); + } else { + // create display specification + const ds: DisplaySpecification = { + displayMode: DisplayModeType.displayAsObject, + viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification), + }; + // update display specification + return dispatch(postProcessDisplaySpecificationActionCreator(vPath, { }, ds)); + } + + } else if (!restResult.data) { // special case: if this is a list without any response if (extractList && restResult.status === 404) { if (!isViewElementList(viewElement!)) { @@ -360,6 +396,9 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async let currentNS: string | null = null; let defaultNS: string | null = null; + let newData: any = null; + let newElement: any = null; + dispatch(new SetCollectingSelectionData(true)); try { for (let ind = 0; ind < pathParts.length; ++ind) { @@ -371,24 +410,40 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`]; if (!viewElement) throw Error('Property [' + property + '] does not exist.'); - if (isViewElementList(viewElement) && !key) { + if (newElement) { + // update view data + if (pathParts.length - 1 === ind) { + newElement[property] = data; + return dispatch(new UpdateNewData(newData)); + } else { + newElement[property] = Array.isArray(newElement[property]) ? [ ...newElement[property] ] : { ...newElement[property] }; + } + } else if (isViewElementList(viewElement) && !key) { embedList = true; if (viewElement && viewElement.isList && viewSpecification.parentView === '0') { - throw new Error('Found a list at root level of a module w/o a refenrece key.'); + throw new Error('Found a list at root level of a module w/o a reference key.'); } - if (pathParts.length - 1 > ind) { + + if (key === null) { + // set new data + const stateData = getState().configuration.viewDescription.newData; + newElement = newData = Array.isArray(stateData) ? [ ...stateData ] : { ...stateData }; + + if (vPath.endsWith('[]') && pathParts.length - 1 === ind) { + // handle new element with any number of arguments + let keyList = viewElement.key?.split(' '); + let dataPathParam = keyList?.map(id => data[id]).join(','); + key = viewElement.key && String(dataPathParam) || ''; + isNew = key; + if (!key) { + dispatch(new SetCollectingSelectionData(false)); + throw new Error('No value for key [' + viewElement.key + '] in list [' + property + ']'); + } + } + + } else if (pathParts.length - 1 > ind) { dispatch(new SetCollectingSelectionData(false)); throw new Error('No key for list [' + property + ']'); - } else if (vPath.endsWith('[]') && pathParts.length - 1 === ind) { - // handle new element with any number of arguments - let keyList = viewElement.key?.split(' '); - let dataPathParam = keyList?.map(id => data[id]).join(','); - key = viewElement.key && String(dataPathParam) || ''; - isNew = key; - if (!key) { - dispatch(new SetCollectingSelectionData(false)); - throw new Error('No value for key [' + viewElement.key + '] in list [' + property + ']'); - } } } @@ -410,7 +465,7 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async const [nsOrName, name] = cur.split(':', 1); const element = pViewSpecification.elements[cur] || pViewSpecification.elements[nsOrName] || pViewSpecification.elements[name]; if (!element && process.env.NODE_ENV === 'development' ) { - throw new Error('removeReadOnlyElements: Could not determine elment for data.'); + throw new Error('removeReadOnlyElements: Could not determine element for data.'); } if (element && element.config) { if (element.uiType === 'object') { @@ -436,7 +491,7 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async : data; // embed the first element list[key] - data = isNew + data = isNew || newData ? [data] : data; @@ -449,6 +504,10 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async } } + if (newData) { + dispatch(new UpdateNewData(null)); + } + if (isNew) { return dispatch(new ReplaceAction(`/configuration/${nodeId}/${vPath.replace(/\[\]$/i, `[${isNew}]`)}`)); // navigate to new element } diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts b/sdnr/wt-odlux/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts index 39b47be84..4d361bf09 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts @@ -18,7 +18,7 @@ import { IActionHandler } from '../../../../framework/src/flux/action'; -import { UpdateViewDescription, UpdateOutputData } from '../actions/deviceActions'; +import { UpdateViewDescription, UpdateOutputData, UpdateNewData } from '../actions/deviceActions'; import { ViewSpecification } from '../models/uiModels'; export enum DisplayModeType { @@ -50,6 +50,7 @@ export type DisplaySpecification = { export interface IViewDescriptionState { vPath: string | null; displaySpecification: DisplaySpecification; + newData?: any; viewData: any; outputData?: any; } @@ -77,6 +78,11 @@ export const viewDescriptionHandler: IActionHandler<IViewDescriptionState> = (st ...state, outputData: action.outputData, }; + } else if (action instanceof UpdateNewData) { + state = { + ...state, + newData: action.newData, + }; } return state; }; diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/models/uiModels.ts b/sdnr/wt-odlux/odlux/apps/configurationApp/src/models/uiModels.ts index 7d9e63caf..c839f1c91 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/models/uiModels.ts +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/models/uiModels.ts @@ -174,11 +174,11 @@ export const isViewElementBoolean = (viewElement: ViewElement): viewElement is V }; export const isViewElementObject = (viewElement: ViewElement): viewElement is ViewElementObject => { - return viewElement && viewElement.uiType === 'object' && viewElement.isList === false; + return viewElement && viewElement.uiType === 'object' && !viewElement.isList; }; export const isViewElementList = (viewElement: ViewElement): viewElement is ViewElementList => { - return viewElement && viewElement.uiType === 'object' && viewElement.isList === true; + return viewElement && viewElement.uiType === 'object' && !!viewElement.isList; }; export const isViewElementObjectOrList = (viewElement: ViewElement): viewElement is ViewElementObject | ViewElementList => { diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/services/restServices.ts b/sdnr/wt-odlux/odlux/apps/configurationApp/src/services/restServices.ts index 07e263559..0fcd94567 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/services/restServices.ts +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/services/restServices.ts @@ -143,8 +143,8 @@ class RestService { * @param data The data to be updated. * @returns The written data. */ - public setConfigData(path: string, data: any) { - return requestRestExt<{ [key: string]: any }>(path, { method: 'PUT', body: JSON.stringify(data) }); + public setConfigData(path: string, data: any, method: 'PUT' | 'POST' = 'PUT') { + return requestRestExt<{ [key: string]: any }>(path, { method, body: JSON.stringify(data) }); } public executeRpc(path: string, data: any) { diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts b/sdnr/wt-odlux/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts index ad34c83b9..9a1793671 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts @@ -10,6 +10,7 @@ import { isViewElementRpc, isViewElementChoice, ViewElementChoiceCase, + isViewElementObject, } from '../models/uiModels'; import { Module } from '../models/yang'; @@ -47,13 +48,13 @@ export const resolveVPath = (current: string, vPath: string): string => { return parts.join('/'); }; -export const splitVPath = (vPath: string, vPathParser : RegExp): [string, string?][] => { - const pathParts: [string, string?][] = []; +export const splitVPath = (vPath: string, vPathParser : RegExp): [string, (string | undefined | null)][] => { + const pathParts: [string, (string | undefined | null)][] = []; let partMatch: RegExpExecArray | null; if (vPath) do { partMatch = vPathParser.exec(vPath); if (partMatch) { - pathParts.push([partMatch[1], partMatch[2] || undefined]); + pathParts.push([partMatch[1], partMatch[2] || (partMatch[0].includes('[]') ? null : undefined)]); } } while (partMatch); return pathParts; @@ -321,4 +322,30 @@ export const filterViewElements = async (vPath: string, viewData: any, viewSpeci } return acc; }, Promise.resolve({ ...viewSpecification, elements: {} as { [key: string]: ViewElement } })); -};
\ No newline at end of file +}; + +export const createViewData = (namespace: string | null, viewSpecification: ViewSpecification, views: ViewSpecification[]) => Object.keys(viewSpecification.elements).reduce<{ [name: string]: any }>((acc, cur) => { + const elm = viewSpecification.elements[cur]; + let currentNamespace = namespace; + const key = elm.id; + if (elm.default) { + acc[key] = elm.default || ''; + } else if (elm.uiType === 'boolean') { + acc[key] = false; + } else if (elm.uiType === 'number') { + acc[key] = 0; + } else if (elm.uiType === 'string') { + acc[key] = ''; + } else if (isViewElementObject(elm)) { + const view = views[+elm.viewId]; + if (view) { + if (view.ns) { + currentNamespace = view.ns; + } + acc[key] = createViewData(currentNamespace, view, views); + } + } else if (isViewElementList(elm)) { + acc[key] = []; + } + return acc; +}, {}); diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt-odlux/odlux/apps/configurationApp/src/views/configurationApplication.tsx index 0f143d818..c8a518b9c 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/views/configurationApplication.tsx +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/views/configurationApplication.tsx @@ -66,6 +66,7 @@ import AddIcon from '@mui/icons-material/Add'; import PostAdd from '@mui/icons-material/PostAdd'; import ArrowBack from '@mui/icons-material/ArrowBack'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; +import CheckIcon from '@mui/icons-material/Check'; import SaveIcon from '@mui/icons-material/Save'; import EditIcon from '@mui/icons-material/Edit'; import Tooltip from '@mui/material/Tooltip'; @@ -202,6 +203,7 @@ type ConfigurationApplicationComponentProps = RouteComponentProps & Connect<type type ConfigurationApplicationComponentState = { isNew: boolean; + isNewSubElement: boolean; editMode: boolean; canEdit: boolean; viewData: { [key: string]: any } | null; @@ -235,6 +237,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp this.state = { isNew: false, + isNewSubElement: false, canEdit: false, editMode: false, viewData: null, @@ -283,11 +286,14 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp static getDerivedStateFromProps(nextProps: ConfigurationApplicationComponentProps, prevState: ConfigurationApplicationComponentState & { [OldProps]: ConfigurationApplicationComponentProps }) { if (!prevState || !prevState[OldProps] || (prevState[OldProps].viewData !== nextProps.viewData)) { - const isNew: boolean = nextProps.vPath?.endsWith('[]') || false; + const isNew: boolean = nextProps.vPath?.includes('[]') || false; + const isNewSubElement: boolean = nextProps.vPath?.includes('[]') && !nextProps.vPath?.endsWith('[]') || false; + const state = { ...prevState, isNew: isNew, editMode: isNew, + isNewSubElement: isNewSubElement, viewData: nextProps.viewData || null, [OldProps]: nextProps, choices: nextProps.displaySpecification.displayMode === DisplayModeType.doNotDisplay @@ -543,7 +549,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp ? ( <div className={classes.section}> {sections.references.map(element => ( - <UIElementReference key={element.id} element={element} disabled={editMode || this.isPolicyViewElementForbidden(element, dataPath)} onOpenReference={(elm) => { this.navigate(`/${elm.id}`); }} /> + <UIElementReference key={element.id} element={element} disabled={!isNew && (editMode || this.isPolicyViewElementForbidden(element, dataPath))} onOpenReference={(elm) => { this.navigate(`/${elm.id}`); }} /> ))} </div> ) : null @@ -698,12 +704,12 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp ev.preventDefault(); let keyId = ''; if (listKeyProperty && listKeyProperty.split(' ').length > 1) { - keyId += listKeyProperty.split(' ').map(id => row[id]).join(','); + keyId += listKeyProperty.split(' ').map(id => encodeURIComponent(String(row[id]))).join(','); } else { - keyId = row[listKeyProperty]; + keyId = encodeURIComponent(String(row[listKeyProperty])); } if (listKeyProperty) { - navigate(`[${encodeURIComponent(keyId)}]`); // Do not navigate without key. + navigate(`[${keyId}]`); // Do not navigate without key. } }} ></SelectElementTable> ); @@ -774,11 +780,13 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp } private renderBreadCrumps() { - const { editMode } = this.state; + const { editMode, isNew, isNewSubElement } = this.state; const { displaySpecification, vPath, nodeId } = this.props; const pathParts = splitVPath(vPath!, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key + let lastPath = '/configuration'; let basePath = `/configuration/${nodeId}`; + return ( <div className={this.props.classes.header}> <div> @@ -827,7 +835,13 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp {this.state.editMode && ( <Fab color="secondary" aria-label="back-button" className={this.props.classes.fab} onClick={async () => { if (this.props.vPath) { - await this.props.reloadView(this.props.vPath); + if (isNewSubElement || isNew) { + const index = this.props.vPath.lastIndexOf('[]'); + const newVPath = this.props.vPath.substring(0, index + ( isNewSubElement ? 2 : 0 )); + this.props.history.replace(`/configuration/${nodeId}/${newVPath}`); + } else { + await this.props.reloadView(this.props.vPath); + } } this.setState({ editMode: false }); }} ><ArrowBack /></Fab> @@ -835,15 +849,21 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp { /* do not show edit if this is a list or it can't be edited */ displaySpecification.displayMode === DisplayModeType.displayAsObject && displaySpecification.viewSpecification.canEdit && (<div> <Fab color="secondary" aria-label={editMode ? 'save-button' : 'edit-button'} className={this.props.classes.fab} onClick={() => { - if (this.state.editMode) { + if (this.state.editMode && this.props.vPath) { // ensure only active choices will be contained const resultingViewData = this.collectData(displaySpecification.viewSpecification.elements); this.props.onUpdateData(this.props.vPath!, resultingViewData); + + const index = this.props.vPath.lastIndexOf('[]'); + const newVPath = this.props.vPath.substring(0, index + ( isNewSubElement ? 2 : 0 )); + this.props.history.replace(`/configuration/${nodeId}/${newVPath}`); } this.setState({ editMode: !editMode }); }}> - {editMode - ? <SaveIcon /> + { editMode + ? isNewSubElement + ? <CheckIcon /> + : <SaveIcon /> : <EditIcon /> } </Fab> @@ -919,6 +939,7 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp } render() { + console.log('ConfigurationApplication.render()', this.props); return this.props.collectingData || !this.state.viewData ? this.renderCollectingData() : this.props.listSpecification diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/whenParser.ts b/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/whenParser.ts index fa2968c9c..4956b13a9 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/whenParser.ts +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/whenParser.ts @@ -3,6 +3,7 @@ enum WhenTokenType { OR = 'OR', NOT = 'NOT', EQUALS = 'EQUALS', + NOT_EQUALS = 'NOT_EQUALS', COMMA = 'COMMA', STRING = 'STRING', FUNCTION = 'FUNCTION', @@ -17,9 +18,31 @@ type Token = { value: string; }; -const isAlpha = (char: string) => /[a-z]/i.test(char); +const isAlpha = (char: string) => { + if (!char) return false; + const code = char.charCodeAt(0); + return (code >= 65 && code <= 90) || (code >= 97 && code <= 122); +}; + +const isAlphaNumeric = (char: string) => { + if (!char) return false; + const code = char.charCodeAt(0); + return ( + isAlpha(char) || + (code >= 48 && code <= 57) || + code === 95 || // underscore + code === 45 || // hyphen + code === 47 || // slash + code === 58 || // colon + code === 46 // dot + ); +}; -const isAlphaNumeric = (char: string) => /[A-Za-z0-9_\-/:\.]/i.test(char); +const isOperator = (char: string) => { + if (!char) return false; + const code = char.charCodeAt(0); + return code === 33 || code === 38 || code === 124 || code === 61; +}; const lex = (input: string) : Token[] => { let tokens = [] as any[]; @@ -110,6 +133,7 @@ const lex = (input: string) : Token[] => { continue; } + if (isAlphaNumeric(char)) { let value = ''; while (isAlphaNumeric(char)) { @@ -120,6 +144,36 @@ const lex = (input: string) : Token[] => { tokens.push({ type: WhenTokenType.IDENTIFIER, value }); continue; } + + if (isOperator(char)) { + let value = ''; + while (isOperator(char)) { + value += char; + char = input[++current]; + } + + switch (value) { + case '&&': + tokens.push({ type: WhenTokenType.AND }); + break; + case '||': + tokens.push({ type: WhenTokenType.OR }); + break; + case '!': + tokens.push({ type: WhenTokenType.NOT }); + break; + case '==': + tokens.push({ type: WhenTokenType.EQUALS }); + break; + case '!=': + tokens.push({ type: WhenTokenType.NOT_EQUALS }); + break; + default: + throw new TypeError(`I don't know what this operator is: ${value}`); + } + continue; + } + throw new TypeError(`I don't know what this character is: ${char}`); } return tokens; diff --git a/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/yangParser.ts index 2dbbae274..10f12ddf6 100644 --- a/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/yangParser.ts +++ b/sdnr/wt-odlux/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -491,21 +491,12 @@ export class YangParser { } public postProcess() { - - // execute all post processes like resolving in proper order - this._unionsToResolve.forEach(cb => { + // process all type refs + this._typeRefToResolve.forEach(cb => { try { cb(); } catch (error) { console.warn(error.message); } }); - - // process all groupings - this._groupingsToResolve.filter(vs => vs.uses && vs.uses[ResolveFunction]).forEach(vs => { - try { vs.uses![ResolveFunction] !== undefined && vs.uses![ResolveFunction]!('|'); } catch (error) { - console.warn(`Error resolving: [${vs.name}] [${error.message}]`); - } - }); - /** * This is to fix the issue for sequential execution of modules based on their child and parent relationship * We are sorting the module object based on their augment status @@ -594,7 +585,7 @@ export class YangParser { const identity = module.identities[idKey]; if (identity.base != null) { const base = this.resolveIdentity(identity.base, module); - base.children?.push(identity); + base?.children?.push(identity); } else { baseIdentities.push(identity); } @@ -610,20 +601,31 @@ export class YangParser { } }); - this._typeRefToResolve.forEach(cb => { + this._modulesToResolve.forEach(cb => { try { cb(); } catch (error) { console.warn(error.message); } }); - this._modulesToResolve.forEach(cb => { + // execute all post processes like resolving in proper order + this._unionsToResolve.forEach(cb => { try { cb(); } catch (error) { console.warn(error.message); } }); + // process all groupings + this._groupingsToResolve.filter(vs => vs.uses && vs.uses[ResolveFunction]).forEach(vs => { + try { vs.uses![ResolveFunction] !== undefined && vs.uses![ResolveFunction]!('|'); } catch (error) { + console.warn(`Error resolving: [${vs.name}] [${error.message}]`); + } + }); + + const knownViews: ViewSpecification[] = []; // resolve readOnly const resolveReadOnly = (view: ViewSpecification, parentConfig: boolean) => { + if (knownViews.includes(view)) return; + knownViews.push(view); // update view config view.config = view.config && parentConfig; diff --git a/sdnr/wt-odlux/odlux/apps/connectApp/src/components/networkElements.tsx b/sdnr/wt-odlux/odlux/apps/connectApp/src/components/networkElements.tsx index 1ce8f0c3b..fed176c2c 100644 --- a/sdnr/wt-odlux/odlux/apps/connectApp/src/components/networkElements.tsx +++ b/sdnr/wt-odlux/odlux/apps/connectApp/src/components/networkElements.tsx @@ -199,8 +199,9 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement { property: 'port', title: 'Port', type: ColumnType.numeric }, { property: 'isRequired', title: 'Required', type: ColumnType.boolean }, { property: 'deviceType', title: 'Type', type: ColumnType.text }, - // { property: "coreModelCapability", title: "Core Model", type: ColumnType.text }, { property: 'deviceFunction', title: 'Function', type: ColumnType.text, width: '25%' }, + { property: "coreModelCapability", title: "Core Model", type: ColumnType.text }, + { property: "mountMethod", title: "Mount Method", type: ColumnType.text }, ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus createContextMenu={rowData => { return this.getContextMenu(rowData); diff --git a/sdnr/wt-odlux/odlux/apps/connectApp/src/models/networkElementConnection.ts b/sdnr/wt-odlux/odlux/apps/connectApp/src/models/networkElementConnection.ts index bb076c720..6a5d00dae 100644 --- a/sdnr/wt-odlux/odlux/apps/connectApp/src/models/networkElementConnection.ts +++ b/sdnr/wt-odlux/odlux/apps/connectApp/src/models/networkElementConnection.ts @@ -41,6 +41,7 @@ export type NetworkElementConnection = { capability: string; }[]; }; + mountMethod?: string; }; diff --git a/sdnr/wt-odlux/odlux/apps/faultApp/src/components/dashboardHome.tsx b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/dashboardHome.tsx index a3e32c42c..534a14336 100644 --- a/sdnr/wt-odlux/odlux/apps/faultApp/src/components/dashboardHome.tsx +++ b/sdnr/wt-odlux/odlux/apps/faultApp/src/components/dashboardHome.tsx @@ -153,7 +153,7 @@ class DashboardHome extends React.Component<HomeComponentProps> { let label = (data.datasets[tooltipItem.datasetIndex].labels && data.datasets[tooltipItem.datasetIndex].labels[ - tooltipItem.index + tooltipItem.index ]) || data.labels[tooltipItem.index] || ''; @@ -180,10 +180,12 @@ class DashboardHome extends React.Component<HomeComponentProps> { }, }, onClick: (event: MouseEvent, item: any) => { - if (item[0]) { - let connectionStatus = labels[item[0]._index] + ''; - this.props.navigateToApplication('connect', '/connectionStatus/' + connectionStatus); - } + setTimeout(() => { + if (item[0]) { + let connectionStatus = labels[item[0]._index] + ''; + this.props.navigateToApplication('connect', '/connectionStatus/' + connectionStatus); + } + }, 0); }, }; @@ -263,7 +265,7 @@ class DashboardHome extends React.Component<HomeComponentProps> { let label = (data.datasets[tooltipItem.datasetIndex].labels && data.datasets[tooltipItem.datasetIndex].labels[ - tooltipItem.index + tooltipItem.index ]) || data.labels[tooltipItem.index] || ''; @@ -290,10 +292,12 @@ class DashboardHome extends React.Component<HomeComponentProps> { }, }, onClick: (event: MouseEvent, item: any) => { - if (item[0]) { - let severity = alarmLabels[item[0]._index] + ''; - this.props.navigateToApplication('fault', '/alarmStatus/' + severity); - } + setTimeout(() => { + if (item[0]) { + let severity = alarmLabels[item[0]._index] + ''; + this.props.navigateToApplication('fault', '/alarmStatus/' + severity); + } + }, 0); }, }; |