diff options
author | KAPIL SINGAL <ks220y@att.com> | 2020-02-02 19:27:46 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2020-02-02 19:27:46 +0000 |
commit | 1584ac712f243707197128b3e3d344e435bd2f31 (patch) | |
tree | 70ed41e168dd155a85010167a81a932967097345 /sdnr/wt/odlux/apps | |
parent | 33b86ad077a2e472df5d4ed15227298a238eb8ae (diff) | |
parent | 05ef023752abdb4f1e072332496dc7c6eaff8965 (diff) |
Merge "SDN-R add updated odlux"
Diffstat (limited to 'sdnr/wt/odlux/apps')
49 files changed, 1840 insertions, 359 deletions
diff --git a/sdnr/wt/odlux/apps/apiDemo/pom.xml b/sdnr/wt/odlux/apps/apiDemo/pom.xml index c59a84232..5b6cfa389 100644 --- a/sdnr/wt/odlux/apps/apiDemo/pom.xml +++ b/sdnr/wt/odlux/apps/apiDemo/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-apiDemo</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -111,10 +111,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/app-feature/pom.xml b/sdnr/wt/odlux/apps/app-feature/pom.xml index 8888a7590..d95101681 100644 --- a/sdnr/wt/odlux/apps/app-feature/pom.xml +++ b/sdnr/wt/odlux/apps/app-feature/pom.xml @@ -26,7 +26,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>feature</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>ccsdk-features :: ${project.artifactId} :: feature</name> <licenses> <license> <name>Apache License, Version 2.0</name> diff --git a/sdnr/wt/odlux/apps/configurationApp/pom.xml b/sdnr/wt/odlux/apps/configurationApp/pom.xml index 305630d4d..9681555db 100644 --- a/sdnr/wt/odlux/apps/configurationApp/pom.xml +++ b/sdnr/wt/odlux/apps/configurationApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-configurationApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts index fc0665325..5b4498df8 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts @@ -202,7 +202,7 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: if (ind === 0) { defaultNS = namespace }; - viewElement = viewSpecification.elements[property]; + viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`]; if (!viewElement) throw Error("Property [" + property + "] does not exist."); if (viewElement.isList && !key) { diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts new file mode 100644 index 000000000..ec49191ce --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts @@ -0,0 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { ViewElement } from "../models/uiModels"; + +export type baseProps = { value: ViewElement, inputValue: string, readOnly: boolean, disabled: boolean, onChange(newValue: string): void };
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx new file mode 100644 index 000000000..67358885c --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx @@ -0,0 +1,61 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { ViewElementBase } from "models/uiModels"; +import { TextField, InputAdornment, Input, Tooltip, Divider, IconButton, InputBase, Paper, makeStyles, Theme, createStyles, FormControl, InputLabel, FormHelperText } from "@material-ui/core"; +import * as React from 'react'; +import { faAdjust } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { InputProps } from "@material-ui/core/Input"; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + iconDark: { + color: '#ff8800' + }, + iconLight: { + color: 'orange' + }, + padding: { + paddingLeft: 10, + paddingRight: 10 + }, + }), +); + +type ifwhenProps = { element: ViewElementBase, id: any, label: any, style: any, helperText: string, error: boolean, toogleTooltip(value: boolean): void, [x: string]: any }; + +export const IfWhenTextInput = (props: ifwhenProps) => { + + const { element, toogleTooltip, id, label, helperText: errorText, error, style, ...otherProps } = props; + const classes = useStyles(); + + + const ifFeature = element.ifFeature ? <Tooltip onMouseMove={e => props.toogleTooltip(false)} onMouseOut={e => props.toogleTooltip(true)} title={element.ifFeature}><InputAdornment position="start"><FontAwesomeIcon icon={faAdjust} className={classes.iconDark}></FontAwesomeIcon></InputAdornment></Tooltip> : null; + const whenFeature = element.when ? (<Tooltip className={classes.padding} onMouseMove={e => props.toogleTooltip(false)} onMouseOut={e => props.toogleTooltip(true)} title={element.when}> + <InputAdornment className={classes.padding} position="end"><FontAwesomeIcon icon={faAdjust} className={classes.iconLight}></FontAwesomeIcon></InputAdornment></Tooltip>) : null; + + return ( + <FormControl error={error} style={style}> + <InputLabel htmlFor={id} >{label}</InputLabel> + <Input {...otherProps} id={id} endAdornment={<div>{ifFeature}{whenFeature}</div>} /> + <FormHelperText>{errorText}</FormHelperText> + </FormControl> + + ); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx new file mode 100644 index 000000000..cc141ee35 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx @@ -0,0 +1,58 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { ViewElementBoolean } from "../models/uiModels"; +import * as React from "react" +import { MenuItem, FormHelperText, Select, FormControl, InputLabel } from "@material-ui/core"; +import { baseProps } from "./baseProps"; + +type booleanInputProps = baseProps; + +export const UiElementBoolean = (props: booleanInputProps) => { + + const element = props.value as ViewElementBoolean; + + let error = ""; + const value = String(props.inputValue).toLowerCase(); + if (element.mandatory && value !== "true" && value !== "false") { + error = "Error"; + } + return (!props.readOnly || element.id != null + ? (<FormControl style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + <InputLabel htmlFor={`select-${element.id}`} >{element.label}</InputLabel> + <Select + required={!!element.mandatory} + error={!!error} + onChange={(e) => { props.onChange(e.target.value as any) }} + readOnly={props.readOnly} + disabled={props.disabled} + value={value} + inputProps={{ + name: element.id, + id: `select-${element.id}`, + }} + > + <MenuItem value={'true'}>{element.trueValue || 'True'}</MenuItem> + <MenuItem value={'false'}>{element.falseValue || 'False'}</MenuItem> + + </Select> + <FormHelperText>{error}</FormHelperText> + </FormControl>) + : null + ); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx new file mode 100644 index 000000000..cf462052e --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx @@ -0,0 +1,77 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { ViewElementNumber } from "models/uiModels"; +import { Tooltip, InputAdornment } from "@material-ui/core"; +import * as React from 'react'; +import { baseProps } from "./baseProps"; +import { IfWhenTextInput } from "./ifWhenTextInput"; +import { checkRange } from "./verifyer"; + +type numberInputProps = baseProps; + +export const UiElementNumber = (props: numberInputProps) => { + + + const [error, setError] = React.useState(false); + const [helperText, setHelperText] = React.useState(""); + const [isTooltipVisible, setTooltipVisibility] = React.useState(true); + + const element = props.value as ViewElementNumber; + + const verifyValue = (data: string) => { + + if (data.trim().length > 0) { + const num = Number(data); + if (!isNaN(num)) { + const result = checkRange(element, num); + if (result.length > 0) { + setError(true); + setHelperText(result); + } else { + setError(false); + setHelperText(""); + } + } else { + setError(true); + setHelperText("Input is not a number."); + } + } else { + setError(false); + setHelperText(""); + } + + props.onChange(data); + } + + return ( + <Tooltip title={isTooltipVisible ? element.description || '' : ''}> + <IfWhenTextInput element={element} toogleTooltip={(val: boolean) => setTooltipVisibility(val)} + spellCheck={false} autoFocus margin="dense" + id={element.id} label={element.label} type="text" value={props.inputValue} + style={{ width: 485, marginLeft: 20, marginRight: 20 }} + onChange={(e: any) => { verifyValue(e.target.value) }} + error={error} + readOnly={props.readOnly} + disabled={props.disabled} + helperText={helperText} + startAdornment={element.units != null ? <InputAdornment position="start">{element.units}</InputAdornment> : undefined} + /> + </Tooltip> + ); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx new file mode 100644 index 000000000..2760eee50 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementReference.tsx @@ -0,0 +1,48 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import React from 'react'; +import { Tooltip, Button, FormControl, Theme, createStyles, makeStyles } from '@material-ui/core'; + +import { ViewElement } from '../models/uiModels'; + +const useStyles = makeStyles((theme: Theme) => createStyles({ + button: { + "justifyContent": "left" + }, +})); + +type UIElementReferenceProps = { + element: ViewElement; + disabled: boolean; + onOpenReference(element: ViewElement): void; +}; + +export const UIElementReference: React.FC<UIElementReferenceProps> = (props) => { + const classes = useStyles(); + const { element } = props; + return ( + <FormControl key={element.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + <Tooltip title={element.description || ''}> + <Button className={classes.button} color="secondary" disabled={props.disabled} onClick={() => { + props.onOpenReference(element); + }}>{element.label}</Button> + </Tooltip> + </FormControl> + ); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx new file mode 100644 index 000000000..d46076338 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx @@ -0,0 +1,59 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import * as React from 'react'; +import { baseProps } from './baseProps'; +import { ViewElementSelection } from '../models/uiModels' +import { FormControl, InputLabel, Select, FormHelperText, MenuItem } from '@material-ui/core'; + + + +type selectionProps = baseProps; + +export const UiElementSelection = (props: selectionProps) => { + + const element = props.value as ViewElementSelection; + + let error = ""; + const value = String(props.inputValue).toLowerCase(); + if (element.mandatory && !!value) { + error = "Error"; + } + + return (props.readOnly || props.inputValue != null + ? (<FormControl style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + <InputLabel htmlFor={`select-${element.id}`} >{element.label}</InputLabel> + <Select + required={!!element.mandatory} + error={!!error} + onChange={(e) => { props.onChange(e.target.value as string) }} + readOnly={props.readOnly} + disabled={props.disabled} + value={value.toString().toLowerCase()} + inputProps={{ + name: element.id, + id: `select-${element.id}`, + }} + > + {element.options.map(option => (<MenuItem key={option.key} title={option.description} value={option.key}>{option.key}</MenuItem>))} + </Select> + <FormHelperText>{error}</FormHelperText> + </FormControl>) + : null + ); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx new file mode 100644 index 000000000..43c60b502 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx @@ -0,0 +1,84 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { Tooltip, TextField } from "@material-ui/core"; +import { ViewElementString } from "../models/uiModels"; +import * as React from "react" +import { baseProps } from "./baseProps"; +import { IfWhenTextInput } from "./ifWhenTextInput"; +import { checkRange, checkPattern } from "./verifyer"; + +type stringEntryProps = baseProps & { isKey: boolean }; + +export const UiElementString = (props: stringEntryProps) => { + + const [isError, setError] = React.useState(false); + const [helperText, setHelperText] = React.useState(""); + const [isTooltipVisible, setTooltipVisibility] = React.useState(true); + + const element = props.value as ViewElementString; + + const verifyValues = (data: string) => { + + if (data.trim().length > 0) { + + let errorMessage = ""; + const result = checkRange(element, data.length); + + if (result.length > 0) + errorMessage += result; + + + const patternResult = checkPattern(element.pattern, data) + + if (patternResult.error) { + errorMessage += patternResult.error; + } + + if (errorMessage.length > 0) { + setError(true); + setHelperText(errorMessage); + } else { + setError(false); + setHelperText(""); + } + } else { + setError(false); + setHelperText(""); + } + + + props.onChange(data); + + } + + return ( + <Tooltip title={isTooltipVisible ? element.description || '' : ''}> + <IfWhenTextInput element={element} toogleTooltip={(val: boolean) => setTooltipVisibility(val)} + spellCheck={false} autoFocus margin="dense" + id={element.id} label={props.isKey ? "🔑 " + element.label : element.label} type="text" value={props.inputValue} + style={{ width: 485, marginLeft: 20, marginRight: 20 }} + onChange={(e: any) => { verifyValues(e.target.value) }} + error={isError} + readOnly={props.readOnly} + disabled={props.disabled} + helperText={helperText} + /> + </Tooltip> + ); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx new file mode 100644 index 000000000..dc158c05a --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementUnion.tsx @@ -0,0 +1,94 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import * as React from 'react' +import { baseProps } from './baseProps'; +import { Tooltip } from '@material-ui/core'; +import { IfWhenTextInput } from './ifWhenTextInput'; +import { ViewElementUnion, isViewElementString, isViewElementNumber, isViewElementObject, ViewElementNumber } from '../models/uiModels'; +import { checkRange, checkPattern } from './verifyer'; + +type UiElementUnionProps = { isKey: boolean } & baseProps; + +export const UIElementUnion = (props: UiElementUnionProps) => { + + const [isError, setError] = React.useState(false); + const [helperText, setHelperText] = React.useState(""); + const [isTooltipVisible, setTooltipVisibility] = React.useState(true); + + const element = props.value as ViewElementUnion; + + const verifyValues = (data: string) => { + + debugger; + let foundObjectElements = 0; + let errorMessage = ""; + let isPatternCorrect = null; + + for (let i = 0; i < element.elements.length; i++) { + const unionElement = element.elements[i]; + + if (isViewElementNumber(unionElement)) { + + errorMessage = checkRange(unionElement, Number(data)); + + } else if (isViewElementString(unionElement)) { + errorMessage += checkRange(unionElement, data.length); + isPatternCorrect = checkPattern(unionElement.pattern, data).isValid; + + + } else if (isViewElementObject(unionElement)) { + foundObjectElements++; + } + + if (isPatternCorrect || errorMessage.length === 0) { + break; + } + } + + if (errorMessage.length > 0 || isPatternCorrect !== null && !isPatternCorrect) { + setError(true); + setHelperText("Input is wrong."); + } else { + setError(false); + setHelperText(""); + } + + if (foundObjectElements > 0 && foundObjectElements != element.elements.length) { + throw new Error(`The union element ${element.id} can't be changed.`); + + } else { + props.onChange(data); + } + }; + + + + return <Tooltip title={isTooltipVisible ? element.description || '' : ''}> + <IfWhenTextInput element={element} toogleTooltip={(val: boolean) => setTooltipVisibility(val)} + spellCheck={false} autoFocus margin="dense" + id={element.id} label={props.isKey ? "🔑 " + element.label : element.label} type="text" value={props.inputValue} + onChange={(e: any) => { verifyValues(e.target.value) }} + error={isError} + style={{ width: 485, marginLeft: 20, marginRight: 20 }} + readOnly={props.readOnly} + disabled={props.disabled} + helperText={helperText} + /> + </Tooltip>; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts b/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts new file mode 100644 index 000000000..0a95cd8ca --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts @@ -0,0 +1,280 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { Expression, YangRange, Operator, ViewElementNumber, ViewElementString, isViewElementNumber, isViewElementString } from '../models/uiModels'; + +export type validated = { isValid: boolean, error?: string } + +export type validatedRange = { isValid: boolean, error?: string }; + + +const rangeErrorStartNumber = "The entered number must be"; +const rangeErrorinnerMinTextNumber = "greater or equals than"; +const rangeErrorinnerMaxTextNumber = "less or equals than"; +const rangeErrorEndTextNumber = "."; + +const rangeErrorStartString = "The entered text must have"; +const rangeErrorinnerMinTextString = "no more than"; +const rangeErrorinnerMaxTextString = "less than"; +const rangeErrorEndTextString = " characters."; + +let errorMessageStart = ""; +let errorMessageMiddleMinPart = ""; +let errorMessageMiddleMaxPart = ""; +let errorMessageEnd = ""; + + +export function checkRange(element: ViewElementNumber | ViewElementString, data: number): string { + + //let test1: Operator<YangRange> = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ operation: "AND", arguments: [new RegExp("^z", "g"), new RegExp("z$", "g")] }, new RegExp("^abc", "g"), new RegExp("^123", "g")] }, new RegExp("^def", "g"), new RegExp("^ppp", "g"), new RegExp("^aaa", "g")] }; + //let test1: Operator<YangRange> = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ operation: "AND", arguments: [{ min: -5, max: 10 }, { min: -30, max: -20 }] }, { min: 8, max: 15 }] }] }; + //let test1: Operator<YangRange> = { operation: "OR", arguments: [{ operation: "OR", arguments: [{ min: -50, max: -40 }] }, { min: -30, max: -20 }, { min: 8, max: 15 }] }; + //let test1: Operator<YangRange> = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ min: -5, max: 10 }, { min: 17, max: 23 }] }] }; + + const number = data; + + var expression = undefined; + + if (isViewElementString(element)) { + expression = element.length; + + errorMessageStart = rangeErrorStartString; + errorMessageMiddleMaxPart = rangeErrorinnerMaxTextString; + errorMessageMiddleMinPart = rangeErrorinnerMinTextString; + errorMessageEnd = rangeErrorEndTextString; + + } else if (isViewElementNumber(element)) { + expression = element.range; + + errorMessageStart = rangeErrorStartNumber; + errorMessageMiddleMaxPart = rangeErrorinnerMaxTextNumber; + errorMessageMiddleMinPart = rangeErrorinnerMinTextNumber; + errorMessageEnd = rangeErrorEndTextNumber; + } + + if (expression) { + if (isYangOperator(expression)) { + + const errorMessage = getRangeErrorMessages(expression, data); + return errorMessage; + + } else + if (isYangRange(expression)) { + + if (!isNaN(expression.min)) { + if (number < expression.min) { + return `${errorMessageStart} ${errorMessageMiddleMinPart} ${expression.min}${errorMessageEnd}`; + } + } + + if (!isNaN(expression.max)) { + if (number > expression.max) { + return `${errorMessageStart} ${errorMessageMiddleMaxPart} ${expression.max}${errorMessageEnd}`; + } + } + } + } + + + return ""; +} + +function isYangRange(val: YangRange | Operator<YangRange>): val is YangRange { + return (val as YangRange).min !== undefined; +} + +function isYangOperator(val: YangRange | Operator<YangRange>): val is Operator<YangRange> { + return (val as Operator<YangRange>).operation !== undefined; +} + +function getRangeErrorMessagesRecursively(value: Operator<YangRange>, data: number): string[] { + let currentItteration: string[] = []; + console.log(value); + + // itterate over all elements + for (let i = 0; i < value.arguments.length; i++) { + const element = value.arguments[i]; + + let min = undefined; + let max = undefined; + + let isNumberCorrect = false; + + if (isYangRange(element)) { + + //check found min values + if (!isNaN(element.min)) { + if (data < element.min) { + min = element.min; + } else { + isNumberCorrect = true; + } + } + + // check found max values + if (!isNaN(element.max)) { + if (data > element.max) { + max = element.max; + } else { + isNumberCorrect = true; + } + } + + // construct error messages + if (min != undefined) { + currentItteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMinPart} ${min}`); + } else if (max != undefined) { + currentItteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMaxPart} ${max}`); + + } + + } else if (isYangOperator(element)) { + + //get errormessages from expression + const result = getRangeErrorMessagesRecursively(element, data); + if (result.length === 0) { + isNumberCorrect = true; + } + currentItteration = currentItteration.concat(result); + } + + // if its an OR operation, the number has been checked and min/max are empty (thus not violated) + // delete everything found (because at least one found is correct, therefore all are correct) and break from loop + if (min === undefined && max === undefined && isNumberCorrect && value.operation === "OR") { + + currentItteration.splice(0, currentItteration.length); + break; + } + } + + return currentItteration; +} + +function getRangeErrorMessages(value: Operator<YangRange>, data: number): string { + + const currentItteration = getRangeErrorMessagesRecursively(value, data); + + // build complete error message from found parts + let errormessage = ""; + if (currentItteration.length > 1) { + + currentItteration.forEach((element, index) => { + if (index === 0) { + errormessage = createStartMessage(element); + } else if (index === currentItteration.length - 1) { + errormessage += ` ${element}${errorMessageEnd}`; + } else { + errormessage += `, ${element}` + } + }); + } else if (currentItteration.length == 1) { + errormessage = `${createStartMessage(currentItteration[0])}${errorMessageEnd}`; + } + + return errormessage; +} + +function createStartMessage(element: string) { + + //remove leading or or and from text + if (element.startsWith("and")) + element = element.replace("and", ""); + else if (element.startsWith("or")) + element = element.replace("or", ""); + + return `${errorMessageStart} ${element}`; +} + +export const checkPattern = (expression: RegExp | Operator<RegExp> | undefined, data: string): validated => { + + if (expression) { + if (isRegExp(expression)) { + const isValid = expression.test(data); + if (!isValid) + return { isValid: isValid, error: "The input is in a wrong format." }; + + } else if (isRegExpOperator(expression)) { + const result = isPatternValid(expression, data); + + if (!result) { + return { isValid: false, error: "The input is in a wrong format." }; + } + } + } + + return { isValid: true } +} + +function getRegexRecursively(value: Operator<RegExp>, data: string): boolean[] { + let currentItteration: boolean[] = []; + for (let i = 0; i < value.arguments.length; i++) { + const element = value.arguments[i]; + if (isRegExp(element)) { + // if regex is found, add it to list + currentItteration.push(element.test(data)) + } else if (isRegExpOperator(element)) { + //if RegexExpression is found, try to get regex from it + currentItteration = currentItteration.concat(getRegexRecursively(element, data)); + } + } + + if (value.operation === "OR") { + // if one is true, all are true, all found items can be discarded + let result = currentItteration.find(element => element); + if (result) { + return []; + } + } + return currentItteration; +} + +function isPatternValid(value: Operator<RegExp>, data: string): boolean { + + + // get all regex + const result = getRegexRecursively(value, data); + console.log(value); + + + if (value.operation === "AND") { + // if AND operation is executed... + // no element can be false + const check = result.find(element => element !== true); + if (check) + return false; + else + return true; + } else { + // if OR operation is executed... + // ... just one element must be true + const check = result.find(element => element === true); + if (check) + return true; + else + return false; + + } +} + +function isRegExp(val: RegExp | Operator<RegExp>): val is RegExp { + return (val as RegExp).source !== undefined; +} + +function isRegExpOperator(val: RegExp | Operator<RegExp>): val is Operator<RegExp> { + return (val as Operator<RegExp>).operation !== undefined; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts index 04b63d39b..1af699a6b 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { combineActionHandler } from '../../../../framework/src/flux/middleware'; import { IConnectedNetworkElementsState, connectedNetworkElementsActionHandler } from './connectedNetworkElementsHandler'; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts index 6a68242eb..e6b808b80 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; @@ -16,4 +34,3 @@ export const { // set value action, to change a value } = createExternal<NetworkElementConnection>(connectedNetworkElementsSearchHandler, appState => appState.configuration.connectedNetworkElements); -
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts index 3cc27aa95..408399da4 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { Module } from "../models/yang"; import { ViewSpecification } from "../models/uiModels"; import { IActionHandler } from "../../../../framework/src/flux/action"; @@ -25,6 +43,6 @@ export const deviceDescriptionHandler: IActionHandler<IDeviceDescriptionState> = modules: action.modules, views: action.views }; - } + } return state; }; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts index c9e7dd2cc..9af640f16 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { IActionHandler } from "../../../../framework/src/flux/action"; import { ViewSpecification } from "../models/uiModels"; import { EnableValueSelector, SetSelectedValue, UpdateDeviceDescription, SetCollectingSelectionData, UpdatViewDescription } from "../actions/deviceActions"; @@ -23,7 +41,7 @@ export const valueSelectorHandler: IActionHandler<IValueSelectorState> = (state if (action instanceof SetCollectingSelectionData) { state = { ...state, - collectingData: action.busy, + collectingData: action.busy, }; } else if (action instanceof EnableValueSelector) { state = { diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts index 48155ee1e..c1b3350b2 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { IActionHandler } from "../../../../framework/src/flux/action"; import { UpdatViewDescription } from "../actions/deviceActions"; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts index 2575500a3..88f70181c 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + export type NetworkElementConnection = { id?: string; nodeId: string; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts index 441d1281d..7b41c3845 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + export type ViewElementBase = { "id": string; "label": string; @@ -15,7 +33,7 @@ export type ViewElementBase = { // https://tools.ietf.org/html/rfc7950#section-9.8 export type ViewElementBinary = ViewElementBase & { "uiType": "binary"; - "length"?: number; // number of octets + "length"?: Expression<YangRange>; // number of octets } // https://tools.ietf.org/html/rfc7950#section-9.7.4 @@ -29,16 +47,17 @@ export type ViewElementBits = ViewElementBase & { // https://tools.ietf.org/html/rfc7950#section-9 export type ViewElementString = ViewElementBase & { "uiType": "string"; - "pattern"?: string[]; - "length"?: string; + "pattern"?: Expression<RegExp>; + "length"?: Expression<YangRange>; "invertMatch"?: true; } // https://tools.ietf.org/html/rfc7950#section-9.3 export type ViewElementNumber = ViewElementBase & { "uiType": "number"; - "min"?: number; - "max"?: number; + "min": number; + "max": number; + "range"?: Expression<YangRange>; "units"?: string; "format"?: string; "fDigits"?: number; @@ -85,6 +104,16 @@ export type ViewElementReference = ViewElementBase & { "ref": (currentPath: string) => ViewElement | null; } +export type ViewElementUnion = ViewElementBase & { + "uiType": "union"; + "elements": ViewElement[]; +} + +export type ViewElementChoise = ViewElementBase & { + "uiType": "choise"; + "cases": { [name: string]: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } }; +} + export type ViewElement = | ViewElementBits | ViewElementBinary @@ -94,14 +123,16 @@ export type ViewElement = | ViewElementObject | ViewElementList | ViewElementSelection - | ViewElementReference; + | ViewElementReference + | ViewElementUnion + | ViewElementChoise; export const isViewElementString = (viewElement: ViewElement): viewElement is ViewElementString => { return viewElement && viewElement.uiType === "string"; } export const isViewElementNumber = (viewElement: ViewElement): viewElement is ViewElementNumber => { - return viewElement && viewElement.uiType === "number" ; + return viewElement && viewElement.uiType === "number"; } export const isViewElementBoolean = (viewElement: ViewElement): viewElement is ViewElementBoolean => { @@ -128,6 +159,15 @@ export const isViewElementReference = (viewElement: ViewElement): viewElement is return viewElement && viewElement.uiType === "reference"; } +export const isViewElementUnion = (viewElement: ViewElement): viewElement is ViewElementUnion => { + return viewElement && viewElement.uiType === "union"; +} + +export const isViewElementChoise = (viewElement: ViewElement): viewElement is ViewElementChoise => { + return viewElement && viewElement.uiType === "choise"; +} + + export type ViewSpecification = { "id": string; "name": string; @@ -140,3 +180,17 @@ export type ViewSpecification = { "elements": { [name: string]: ViewElement }; readonly "canEdit": boolean; } + +export type YangRange = { + min: number, + max: number, +} + +export type Expression<T> = + | T + | Operator<T>; + +export type Operator<T> = { + operation: "AND" | "OR"; + arguments: Expression<T>[]; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts index 57edf803f..11eb44d92 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { ViewElement, ViewSpecification } from "./uiModels"; export type Token = { @@ -33,7 +51,7 @@ export type Module = { namespace?: string; prefix?: string; identities: { [name: string]: Identity }; - revisions: { [version: string]: Revision } ; + revisions: { [version: string]: Revision }; imports: { [prefix: string]: string }; features: { [feature: string]: { description?: string } }; typedefs: { [type: string]: ViewElement }; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx index 7fd3a97f9..0cab7b793 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import * as React from "react"; import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; @@ -32,7 +50,7 @@ const ConfigurationApplicationRouteAdapter = connect(mapProps, mapDisp)((props: currentNodeId = undefined; currentVirtualPath = undefined; } - },[]); + }, []); if (props.location.pathname !== lastUrl) { // ensure the asynchronus update will only be called once per path lastUrl = props.location.pathname; @@ -59,9 +77,9 @@ const ConfigurationApplicationRouteAdapter = connect(mapProps, mapDisp)((props: const App = withRouter((props: RouteComponentProps) => ( <Switch> - <Route path={`${props.match.url}/:nodeId/*`} component={ ConfigurationApplicationRouteAdapter } /> - <Route path={`${props.match.url}/:nodeId`} component={ ConfigurationApplicationRouteAdapter } /> - <Route path={`${props.match.url}`} component={ NetworkElementSelector } /> + <Route path={`${props.match.url}/:nodeId/*`} component={ConfigurationApplicationRouteAdapter} /> + <Route path={`${props.match.url}/:nodeId`} component={ConfigurationApplicationRouteAdapter} /> + <Route path={`${props.match.url}`} component={NetworkElementSelector} /> <Redirect to={`${props.match.url}`} /> </Switch> )); diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts index 061be05ec..d0ed03a8d 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { requestRest, requestRestExt } from "../../../../framework/src/services/restService"; import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/utilities/yangHelper"; @@ -32,7 +50,7 @@ class RestService { public setConfigData(path: string, data: any) { return requestRestExt<{ [key: string]: any }>(path, { method: "PUT", body: JSON.stringify(data) }); } - } +} export const restService = new RestService(); export default restService;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts index 17a4e43a7..3dc3a8634 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + type YangInfo = [string, (string | null | undefined)]; const cache: { [path: string]: string } = { diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx index 24a4af8b2..9c92ceb6b 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx @@ -16,7 +16,7 @@ * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; import { WithStyles, withStyles, createStyles, Theme } from '@material-ui/core/styles'; @@ -26,11 +26,12 @@ import { IApplicationStoreState } from "../../../../framework/src/store/applicat import MaterialTable, { ColumnModel, ColumnType, MaterialTableCtorType } from "../../../../framework/src/components/material-table"; import { Loader } from "../../../../framework/src/components/material-ui/loader"; -import { SetSelectedValue, splitVPath, updateDataActionAsyncCreator } from "../actions/deviceActions"; -import { ViewSpecification, isViewElementString, isViewElementNumber, isViewElementBoolean, isViewElementObjectOrList, isViewElementSelection } from "../models/uiModels"; +import { SetSelectedValue, splitVPath, updateDataActionAsyncCreator, updateViewActionAsyncCreator } from "../actions/deviceActions"; +import { ViewSpecification, isViewElementString, isViewElementNumber, isViewElementBoolean, isViewElementObjectOrList, isViewElementSelection, isViewElementChoise, ViewElement, ViewElementChoise, isViewElementUnion } from "../models/uiModels"; import Fab from '@material-ui/core/Fab'; import AddIcon from '@material-ui/icons/Add'; +import ArrowBack from '@material-ui/icons/ArrowBack'; import RemoveIcon from '@material-ui/icons/RemoveCircleOutline'; import SaveIcon from '@material-ui/icons/Save'; import EditIcon from '@material-ui/icons/Edit'; @@ -38,7 +39,7 @@ import Tooltip from "@material-ui/core/Tooltip"; import TextField from "@material-ui/core/TextField"; import FormControl from "@material-ui/core/FormControl"; import IconButton from "@material-ui/core/IconButton"; -import Button from "@material-ui/core/Button"; + import InputAdornment from "@material-ui/core/InputAdornment"; import InputLabel from "@material-ui/core/InputLabel"; import Select from "@material-ui/core/Select"; @@ -47,6 +48,13 @@ import Breadcrumbs from "@material-ui/core/Breadcrumbs"; import Link from "@material-ui/core/Link"; import FormHelperText from '@material-ui/core/FormHelperText'; +import { UIElementReference } from '../components/uiElementReference'; +import { UiElementNumber } from '../components/uiElementNumber'; +import { UiElementString } from '../components/uiElementString'; +import { UiElementBoolean } from '../components/uiElementBoolean'; +import { UiElementSelection } from '../components/uiElementSelection'; +import { UIElementUnion } from '../components/uiElementUnion'; + const styles = (theme: Theme) => createStyles({ header: { "display": "flex", @@ -97,6 +105,16 @@ const styles = (theme: Theme) => createStyles({ }, }, }, + section: { + padding: "15px", + borderBottom: `2px solid ${theme.palette.divider}`, + }, + viewElements: { + width: 485, marginLeft: 20, marginRight: 20 + }, + verificationElements: { + width: 485, marginLeft: 20, marginRight: 20 + } }); const mapProps = (state: IApplicationStoreState) => ({ @@ -115,6 +133,7 @@ const mapProps = (state: IApplicationStoreState) => ({ const mapDispatch = (dispatcher: IDispatcher) => ({ onValueSelected: (value: any) => dispatcher.dispatch(new SetSelectedValue(value)), onUpdateData: (vPath: string, data: any) => dispatcher.dispatch(updateDataActionAsyncCreator(vPath, data)), + reloadView: (vPath: string) => dispatcher.dispatch(updateViewActionAsyncCreator(vPath)), }); const SelectElementTable = MaterialTable as MaterialTableCtorType<{ [key: string]: any }>; @@ -126,6 +145,7 @@ type ConfigurationApplicationComponentState = { editMode: boolean; canEdit: boolean; viewData: { [key: string]: any } | null; + choises: { [path: string]: { selectedCase: string, data: { [property: string]: any } } }; } const OldProps = Symbol("OldProps"); @@ -134,27 +154,61 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp /** * */ - constructor (props: ConfigurationApplicationComponentProps) { + constructor(props: ConfigurationApplicationComponentProps) { super(props); this.state = { isNew: false, canEdit: false, editMode: false, - viewData: null + viewData: null, + choises: {}, } } - static getDerivedStateFromProps(nextProps: ConfigurationApplicationComponentProps, prevState: ConfigurationApplicationComponentState & { [OldProps]: ConfigurationApplicationComponentProps }) { + 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 ?.endsWith("[]") || false; const state = { ...prevState, isNew: isNew, editMode: isNew, viewData: nextProps.viewData || null, [OldProps]: nextProps, + choises: nextProps.viewSpecification && Object.keys(nextProps.viewSpecification.elements).reduce((acc, cur) => { + const elm = nextProps.viewSpecification.elements[cur]; + if (isViewElementChoise(elm)) { + const caseKeys = Object.keys(elm.cases); + + // find the right case for this choise, use the first one with data, at least use index 0 + const selectedCase = caseKeys.find(key => { + const caseElm = elm.cases[key]; + return Object.keys(caseElm.elements).some(caseElmKey => { + const caseElmElm = caseElm.elements[caseElmKey]; + return nextProps.viewData[caseElmElm.label] != null || nextProps.viewData[caseElmElm.id] != null; + }); + }) || caseKeys[0]; + + // extract all data of the active case + const caseElements = elm.cases[selectedCase].elements; + const data = Object.keys(caseElements).reduce((dataAcc, dataCur) => { + const dataElm = caseElements[dataCur]; + if (nextProps.viewData[dataElm.label] !== undefined) { + dataAcc[dataElm.label] = nextProps.viewData[dataElm.label]; + } else if (nextProps.viewData[dataElm.id] !== undefined) { + dataAcc[dataElm.id] = nextProps.viewData[dataElm.id]; + } + return dataAcc; + }, {} as { [name: string]: any }); + + acc[elm.id] = { + selectedCase, + data, + }; + } + return acc; + }, {} as { [path: string]: { selectedCase: string, data: { [property: string]: any } } }) || {} } return state; } @@ -174,123 +228,203 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp }); } - private renderUIElement = (viewSpecification: ViewSpecification, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { - const elements = viewSpecification.elements; - return ( - Object.keys(elements).sort((a, b) => { - const vsA = elements[a]; - const vsB = elements[b]; - if (keyProperty) { - // if (vsA.label === vsB.label) return 0; - if (vsA.label === keyProperty) return -1; - if (vsB.label === keyProperty) return +1; - } + private renderUIElement = (uiElement: ViewElement, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { + const isKey = (uiElement.label === keyProperty); + const canEdit = editMode && (isNew || (uiElement.config && !isKey)); + if (isViewElementSelection(uiElement)) { + + return <UiElementSelection + key={uiElement.id} + inputValue={viewData[uiElement.id] || ''} + value={uiElement} + readOnly={!canEdit} + disabled={editMode && !canEdit} + onChange={(e) => { this.changeValueFor(uiElement.id, e) }} + /> + + } else if (isViewElementBoolean(uiElement)) { + return <UiElementBoolean + key={uiElement.id} + inputValue={viewData[uiElement.id] || ''} + value={uiElement} + readOnly={!canEdit} + disabled={editMode && !canEdit} + onChange={(e) => { this.changeValueFor(uiElement.id, e) }} /> + + } else if (isViewElementString(uiElement)) { + return <UiElementString + key={uiElement.id} + inputValue={viewData[uiElement.id] || ''} + value={uiElement} + isKey={isKey} + readOnly={!canEdit} + disabled={editMode && !canEdit} + onChange={(e) => { this.changeValueFor(uiElement.id, e) }} /> + + } else if (isViewElementNumber(uiElement)) { + return <UiElementNumber + key={uiElement.id} + value={uiElement} + inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]} + readOnly={!canEdit} + disabled={editMode && !canEdit} + onChange={(e) => { this.changeValueFor(uiElement.id, e) }} /> + } else if (isViewElementUnion(uiElement)) { + return <UIElementUnion + key={uiElement.id} + isKey={false} + value={uiElement} + inputValue={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]} + readOnly={!canEdit} + disabled={editMode && !canEdit} + onChange={(e) => { this.changeValueFor(uiElement.id, e) }} /> + } + else { + if (process.env.NODE_ENV !== "production") { + console.error(`Unknown element type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + } + return null; + } + }; - if (vsA.uiType === vsB.uiType) return 0; - if (vsA.uiType !== "object" && vsB.uiType !== "object") return 0; - if (vsA.uiType === "object") return +1; - return -1; - }).map(key => { - const uiElement = elements[key]; - const isKey = (uiElement.label === keyProperty); - const canEdit = editMode && (isNew || (uiElement.config && !isKey)); - if (isViewElementSelection(uiElement)) { - let error = "" - const value = String(viewData[uiElement.id]).toLowerCase(); - if (uiElement.mandatory && !!value) { - error = "Error"; - } - return (canEdit || viewData[uiElement.id] != null - ? (<FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> - <InputLabel htmlFor={`select-${uiElement.id}`} >{uiElement.label}</InputLabel> - <Select - required={!!uiElement.mandatory} - error={!!error} - onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} - readOnly={!canEdit} - disabled={editMode && !canEdit} - value={(viewData[uiElement.id] || '').toString().toLowerCase()} - inputProps={{ - name: uiElement.id, - id: `select-${uiElement.id}`, - }} - > - {uiElement.options.map(option => (<MenuItem key={option.key} title={option.description} value={option.value}>{option.key}</MenuItem>))} - </Select> - <FormHelperText>{error}</FormHelperText> - </FormControl>) - : null - ); - } else if (isViewElementBoolean(uiElement)) { - let error = "" - const value = String(viewData[uiElement.id]).toLowerCase(); - if (uiElement.mandatory && value !== "true" && value !== "false") { - error = "Error"; - } - return (canEdit || viewData[uiElement.id] != null - ? (<FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> - <InputLabel htmlFor={`select-${uiElement.id}`} >{uiElement.label}</InputLabel> - <Select - required={!!uiElement.mandatory} - error={!!error} - onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} - readOnly={!canEdit} - disabled={editMode && !canEdit} - value={value} - inputProps={{ - name: uiElement.id, - id: `select-${uiElement.id}`, - }} - > - <MenuItem value={'true'}>{uiElement.trueValue || 'True'}</MenuItem> - <MenuItem value={'false'}>{uiElement.falseValue || 'False'}</MenuItem> - - </Select> - <FormHelperText>{error}</FormHelperText> - </FormControl>) - : null - ); - } else if (isViewElementString(uiElement)) { - return ( - <Tooltip key={uiElement.id} title={uiElement.description || ''}> - <TextField InputProps={{ readOnly: !canEdit, disabled: editMode && !canEdit }} spellCheck={false} autoFocus margin="dense" - id={uiElement.id} label={isKey ? "🔑 " + uiElement.label : uiElement.label} type="text" value={viewData[uiElement.id] || ''} - style={{ width: 485, marginLeft: 20, marginRight: 20 }} - onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} - /> - </Tooltip> - ); - } else if (isViewElementNumber(uiElement)) { - return ( - <Tooltip key={uiElement.id} title={uiElement.description || ''}> - <TextField InputProps={{ readOnly: !canEdit, disabled: editMode && !canEdit, startAdornment: uiElement.units != null ? <InputAdornment position="start">{uiElement.units}</InputAdornment> : undefined }} spellCheck={false} autoFocus margin="dense" - id={uiElement.id} label={uiElement.label} type="text" value={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]} - style={{ width: 485, marginLeft: 20, marginRight: 20 }} - onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} - /> - </Tooltip> - ); - } else if (isViewElementObjectOrList(uiElement)) { - return ( - <FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> - <Tooltip title={uiElement.description || ''}> - <Button className={this.props.classes.leftButton} color="secondary" disabled={this.state.editMode} onClick={() => { - this.navigate(`/${uiElement.id}`); - }}>{uiElement.label}</Button> - </Tooltip> - </FormControl> - ); - } else { - if (process.env.NODE_ENV !== "production") { - console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + // private renderUIReference = (uiElement: ViewElement, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { + // const isKey = (uiElement.label === keyProperty); + // const canEdit = editMode && (isNew || (uiElement.config && !isKey)); + // if (isViewElementObjectOrList(uiElement)) { + // return ( + // <FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + // <Tooltip title={uiElement.description || ''}> + // <Button className={this.props.classes.leftButton} color="secondary" disabled={this.state.editMode} onClick={() => { + // this.navigate(`/${uiElement.id}`); + // }}>{uiElement.label}</Button> + // </Tooltip> + // </FormControl> + // ); + // } else { + // if (process.env.NODE_ENV !== "production") { + // console.error(`Unknown reference type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + // } + // return null; + // } + // }; + + private renderUIChoise = (uiElement: ViewElementChoise, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { + const isKey = (uiElement.label === keyProperty); + + const currentChoise = this.state.choises[uiElement.id]; + const currentCase = currentChoise && uiElement.cases[currentChoise.selectedCase]; + + const canEdit = editMode && (isNew || (uiElement.config && !isKey)); + if (isViewElementChoise(uiElement)) { + const subElements = currentCase ?.elements; + return ( + <> + <FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + <InputLabel htmlFor={`select-${uiElement.id}`} >{uiElement.label}</InputLabel> + <Select + required={!!uiElement.mandatory} + onChange={(e) => { + if (currentChoise.selectedCase === e.target.value) { + return; // nothing changed + } + this.setState({ choises: { ...this.state.choises, [uiElement.id]: { ...this.state.choises[uiElement.id], selectedCase: e.target.value as string } } }); + }} + readOnly={!canEdit} + disabled={editMode && !canEdit} + value={this.state.choises[uiElement.id].selectedCase} + inputProps={{ + name: uiElement.id, + id: `select-${uiElement.id}`, + }} + > + { + Object.keys(uiElement.cases).map(caseKey => { + const caseElm = uiElement.cases[caseKey]; + return ( + <MenuItem key={caseElm.id} title={caseElm.description} value={caseKey}>{caseElm.label}</MenuItem> + ); + }) + } + </Select> + </FormControl> + {subElements + ? Object.keys(subElements).map(elmKey => { + const elm = subElements[elmKey]; + return this.renderUIElement(elm, viewData, keyProperty, editMode, isNew); + }) + : <h3>Invalid Choise</h3> } - return null; + </> + ); + } else { + if (process.env.NODE_ENV !== "production") { + console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + } + return null; + } + }; + + private renderUIView = (viewSpecification: ViewSpecification, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { + const { classes } = this.props; + + const orderFunc = (vsA: ViewElement, vsB: ViewElement) => { + if (keyProperty) { + // if (vsA.label === vsB.label) return 0; + if (vsA.label === keyProperty) return -1; + if (vsB.label === keyProperty) return +1; + } + + // if (vsA.uiType === vsB.uiType) return 0; + // if (vsA.uiType !== "object" && vsB.uiType !== "object") return 0; + // if (vsA.uiType === "object") return +1; + return -1; + }; + + const sections = Object.keys(viewSpecification.elements).reduce((acc, cur) => { + const elm = viewSpecification.elements[cur]; + if (isViewElementObjectOrList(elm)) { + acc.references.push(elm); + } else if (isViewElementChoise(elm)) { + acc.choises.push(elm); + } else { + acc.elements.push(elm); + } + return acc; + }, { elements: [] as ViewElement[], references: [] as ViewElement[], choises: [] as ViewElementChoise[] }); + + sections.elements = sections.elements.sort(orderFunc); + + return ( + <> + <div className={classes.section} /> + {sections.elements.length > 0 + ? ( + <div className={classes.section}> + {sections.elements.map(element => this.renderUIElement(element, viewData, keyProperty, editMode, isNew))} + </div> + ) : null + } + {sections.references.length > 0 + ? ( + <div className={classes.section}> + {sections.references.map(element => ( + <UIElementReference key={element.id} element={element} disabled={editMode} onOpenReference={(elm) => { this.navigate(`/${elm.id}`) }} /> + ))} + </div> + ) : null } - }) + {sections.choises.length > 0 + ? ( + <div className={classes.section}> + {sections.choises.map(element => this.renderUIChoise(element, viewData, keyProperty, editMode, isNew))} + </div> + ) : null + } + </> ); }; - private renderUIElementList(listSpecification: ViewSpecification, listKeyProperty: string, listData: { [key: string]: any }[]) { + private renderUIViewList(listSpecification: ViewSpecification, listKeyProperty: string, listData: { [key: string]: any }[]) { const listElements = listSpecification.elements; const navigate = (path: string) => { @@ -381,11 +515,41 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp } </Breadcrumbs> </div> + {this.state.editMode && ( + <Fab color="secondary" aria-label="edit" className={this.props.classes.fab} onClick={async () => { + this.props.vPath && await this.props.reloadView(this.props.vPath); + this.setState({ editMode: false }); + }} ><ArrowBack /></Fab> + ) || null} { /* do not show edit if this is a list or it can't be edited */ !displayAsList && viewSpecification.canEdit && (<div> <Fab color="secondary" aria-label="edit" className={this.props.classes.fab} onClick={() => { if (this.state.editMode) { - this.props.onUpdateData(this.props.vPath!, this.state.viewData); + + // ensure only active choises will be contained + const choiseKeys = Object.keys(viewSpecification.elements).filter(elmKey => isViewElementChoise(viewSpecification.elements[elmKey])); + const elementsToRemove = choiseKeys.reduce((acc, cur) => { + const choise = viewSpecification.elements[cur] as ViewElementChoise; + const selectedCase = this.state.choises[cur].selectedCase; + Object.keys(choise.cases).forEach(caseKey => { + if (caseKey === selectedCase) return; + const caseElements = choise.cases[caseKey].elements; + Object.keys(caseElements).forEach(caseElementKey => { + acc.push(caseElements[caseElementKey]); + }); + }); + return acc; + }, [] as ViewElement[]); + + const viewData = this.state.viewData; + const resultingViewData = viewData && Object.keys(viewData).reduce((acc, cur) => { + if (!elementsToRemove.some(elm => elm.label === cur || elm.id === cur)) { + acc[cur] = viewData[cur]; + } + return acc; + }, {} as { [key: string]: any }); + + this.props.onUpdateData(this.props.vPath!, resultingViewData); } this.setState({ editMode: !editMode }); }}> @@ -431,10 +595,10 @@ class ConfigurationApplicationComponent extends React.Component<ConfigurationApp return ( <div> - { this.renderBreadCrumps() } - { displayAsList && viewData instanceof Array - ? this.renderUIElementList(viewSpecification, keyProperty!, viewData) - : this.renderUIElement(viewSpecification, viewData!, keyProperty, editMode, isNew) + {this.renderBreadCrumps()} + {displayAsList && viewData instanceof Array + ? this.renderUIViewList(viewSpecification, keyProperty!, viewData) + : this.renderUIView(viewSpecification, viewData!, keyProperty, editMode, isNew) } </div > ); diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx index 6fd5c8cf0..8155becbb 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; @@ -29,7 +47,7 @@ class NetworkElementSelectorComponent extends React.Component<NetworkElementSele render() { return ( - <ConnectedElementTable onHandleClick={(e, row) => { this.props.history.push(`${ this.props.match.path }/${row.nodeId}`) }} columns={[ + <ConnectedElementTable onHandleClick={(e, row) => { this.props.history.push(`${this.props.match.path}/${row.nodeId}`) }} columns={[ { property: "nodeId", title: "Name", type: ColumnType.text }, { property: "isRequired", title: "Required ?", type: ColumnType.boolean }, { property: "host", title: "Host", type: ColumnType.text }, diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts index c7ab5e4a3..49c2b9be7 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -1,6 +1,24 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { Token, Statement, Module, Identity } from "../models/yang"; -import { ViewSpecification, ViewElement, isViewElementObjectOrList, ViewElementBase, isViewElementReference } from "../models/uiModels"; +import { ViewSpecification, ViewElement, isViewElementObjectOrList, ViewElementBase, isViewElementReference, ViewElementChoise, ViewElementBinary, ViewElementString, isViewElementString, isViewElementNumber, ViewElementNumber, Expression, YangRange, ViewElementUnion } from "../models/uiModels"; import { yangService } from "../services/yangService"; export const splitVPath = (vPath: string, vPathParser: RegExp): RegExpMatchArray[] => { @@ -238,6 +256,8 @@ class YangLexer { export class YangParser { private _groupingsToResolve: (() => void)[] = []; private _identityToResolve: (() => void)[] = []; + private _unionsToResolve: (() => void)[] = []; + private _modules: { [name: string]: Module } = {}; private _views: ViewSpecification[] = [{ @@ -248,7 +268,9 @@ export class YangParser { parentView: "0", title: "root", elements: {}, - }]; + }]; + + public static ResolveStack = Symbol("ResolveStack"); constructor() { @@ -311,12 +333,12 @@ export class YangParser { const revisions = this.extractNodes(rootStatement, "revision"); module.revisions = { ...module.revisions, - ...revisions.reduce<{ [version: string]: { }}>((acc, version) => { + ...revisions.reduce<{ [version: string]: {} }>((acc, version) => { if (!version.arg) { throw new Error(`Module [${module.name}] has a version w/o version number.`); } const description = this.extractValue(version, "description"); - const reference = this.extractValue(version,"reference"); + const reference = this.extractValue(version, "reference"); acc[version.arg] = { description, reference, @@ -345,10 +367,10 @@ export class YangParser { const imports = this.extractNodes(rootStatement, "import"); module.imports = { ...module.imports, - ...imports.reduce < { [key: string]: string }>((acc, imp) => { + ...imports.reduce<{ [key: string]: string }>((acc, imp) => { const prefix = imp.sub && imp.sub.filter(s => s.key === "prefix"); if (!imp.arg) { - throw new Error(`Module [${module.name}] has an import with neither name nor prefix.`); + throw new Error(`Module [${module.name}] has an import with neither name nor prefix.`); } acc[prefix && prefix.length === 1 && prefix[0].arg || imp.arg] = imp.arg; return acc; @@ -389,8 +411,15 @@ export class YangParser { } public postProcess() { - // process all groupings + // execute all post processes like resolving in propper order + this._unionsToResolve.forEach(cb => { + try { cb(); } catch (error) { + console.warn(error.message); + } + }); + + // process all groupings this._groupingsToResolve.forEach(cb => { try { cb(); } catch (error) { console.warn(`Error resolving: [${error.message}]`); @@ -418,7 +447,7 @@ export class YangParser { }); // process Identities - const traverseIdentity = (identities : Identity[]) => { + const traverseIdentity = (identities: Identity[]) => { const result: Identity[] = []; for (let identity of identities) { if (identity.children && identity.children.length > 0) { @@ -438,7 +467,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 { baseIdentites.push(identity); } @@ -455,7 +484,6 @@ export class YangParser { }); }; - private _nextId = 1; private get nextId() { return this._nextId++; @@ -478,7 +506,7 @@ export class YangParser { private extractTypeDefinitions(statement: Statement, module: Module, currentPath: string): void { const typedefs = this.extractNodes(statement, "typedef"); typedefs && typedefs.forEach(def => { - if (! def.arg) { + if (!def.arg) { throw new Error(`Module: [${module.name}]. Found typefed without name.`); } module.typedefs[def.arg] = this.getViewElement(def, module, 0, currentPath, false); @@ -562,13 +590,14 @@ export class YangParser { // extract conditions const ifFeature = this.extractValue(statement, "if-feature"); const whenCondition = this.extractValue(statement, "when"); + if (whenCondition) console.warn("Found in [" + module.name + "]" + currentPath + " when: " + whenCondition); // extract all container const container = this.extractNodes(statement, "container"); if (container && container.length > 0) { subViews.push(...container.reduce<ViewSpecification[]>((acc, cur) => { if (!cur.arg) { - throw new Error(`Module: [${module.name}]. Found container without name.`); + throw new Error(`Module: [${module.name}]${currentPath}. Found container without name.`); } const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); elements.push({ @@ -589,11 +618,11 @@ export class YangParser { if (lists && lists.length > 0) { subViews.push(...lists.reduce<ViewSpecification[]>((acc, cur) => { if (!cur.arg) { - throw new Error(`Module: [${module.name}]. Found list without name.`); + throw new Error(`Module: [${module.name}]${currentPath}. Found list without name.`); } const key = this.extractValue(cur, "key") || undefined; if (config && !key) { - throw new Error(`Module: [${module.name}]. Found configurable list without key.`); + throw new Error(`Module: [${module.name}]${currentPath}. Found configurable list without key.`); } const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); elements.push({ @@ -635,10 +664,70 @@ export class YangParser { const choiceStms = this.extractNodes(statement, "choice"); if (choiceStms && choiceStms.length > 0) { - for (let i = 0; i < choiceStms.length; ++i) { - const cases = this.extractNodes(choiceStms[i], "case"); - console.warn(`Choice found ${choiceStms[i].arg}::${cases.map(c => c.arg).join(";")}`, choiceStms[i]); - } + elements.push(...choiceStms.reduce<ViewElementChoise[]>((accChoise, curChoise) => { + if (!curChoise.arg) { + throw new Error(`Module: [${module.name}]${currentPath}. Found choise without name.`); + } + // extract all cases like containers + const cases: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[] = []; + const caseStms = this.extractNodes(curChoise, "case"); + if (caseStms && caseStms.length > 0) { + cases.push(...caseStms.reduce((accCase, curCase) => { + if (!curCase.arg) { + throw new Error(`Module: [${module.name}]${currentPath}/${curChoise.arg}. Found case without name.`); + } + const description = this.extractValue(curCase, "description") || undefined; + const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, module, `${currentPath}/${module.name}:${curChoise.arg}`); + subViews.push(caseView, ...caseSubViews); + + const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = { + id: parentId === 0 ? `${module.name}:${curCase.arg}` : curCase.arg, + label: curCase.arg, + description: description, + elements: caseView.elements + }; + accCase.push(caseDef); + return accCase; + }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[])); + } + + // extract all simple cases (one case per leaf, container, etc.) + const [choiseView, choiseSubViews] = this.extractSubViews(curChoise, parentId, module, `${currentPath}/${module.name}:${curChoise.arg}`); + subViews.push(choiseView, ...choiseSubViews); + cases.push(...Object.keys(choiseView.elements).reduce((accElm, curElm) => { + const elm = choiseView.elements[curElm]; + const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = { + id: elm.id, + label: elm.label, + description: elm.description, + elements: { [elm.id]: elm } + }; + accElm.push(caseDef); + return accElm; + }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[])); + + const description = this.extractValue(curChoise, "description") || undefined; + const configValue = this.extractValue(curChoise, "config"); + const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + + const mandatory = this.extractValue(curChoise, "mandatory") === "true" || false; + + const element: ViewElementChoise = { + uiType: "choise", + id: parentId === 0 ? `${module.name}:${curChoise.arg}` : curChoise.arg, + label: curChoise.arg, + config: config, + mandatory: mandatory, + description: description, + cases: cases.reduce((acc, cur) => { + acc[cur.id] = cur; + return acc; + }, {} as { [name: string]: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } }) + }; + + accChoise.push(element); + return accChoise; + }, [])); } const rpcs = this.extractNodes(statement, "rpc"); @@ -707,17 +796,74 @@ export class YangParser { return [viewSpec, subViews]; } + // https://tools.ietf.org/html/rfc7950#section-9.3.4 + private static decimalRange = [ + { min: -9223372036854775808, max: 9223372036854775807 }, + { min: -922337203685477580.8, max: 922337203685477580.7 }, + { min: -92233720368547758.08, max: 92233720368547758.07 }, + { min: -9223372036854775.808, max: 9223372036854775.807 }, + { min: -922337203685477.5808, max: 922337203685477.5807 }, + { min: -92233720368547.75808, max: 92233720368547.75807 }, + { min: -9223372036854.775808, max: 9223372036854.775807 }, + { min: -922337203685.4775808, max: 922337203685.4775807 }, + { min: -92233720368.54775808, max: 92233720368.54775807 }, + { min: -9223372036.854775808, max: 9223372036.854775807 }, + { min: -922337203.6854775808, max: 922337203.6854775807 }, + { min: -92233720.36854775808, max: 92233720.36854775807 }, + { min: -9223372.036854775808, max: 9223372.036854775807 }, + { min: -922337.2036854775808, max: 922337.2036854775807 }, + { min: -92233.72036854775808, max: 92233.72036854775807 }, + { min: -9223.372036854775808, max: 9223.372036854775807 }, + { min: -922.3372036854775808, max: 922.3372036854775807 }, + { min: -92.23372036854775808, max: 92.23372036854775807 }, + { min: -9.223372036854775808, max: 9.223372036854775807 }, + ]; + /** Extracts the UI View from the type in the cur statement. */ private getViewElement(cur: Statement, module: Module, parentId: number, currentPath: string, isList: boolean): ViewElement { const type = this.extractValue(cur, "type"); const defaultVal = this.extractValue(cur, "default") || undefined; const description = this.extractValue(cur, "description") || undefined; - const rangeMatch = this.extractValue(cur, "range", /^(\d+)\.\.(\d+)/) || undefined; const configValue = this.extractValue(cur, "config"); const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + const extractRange = (min: number, max: number, property: string = "range"): { expression: Expression<YangRange> | undefined, min: number, max: number } => { + const ranges = this.extractValue(this.extractNodes(cur, "type")[0]!, property) || undefined; + const range = ranges ?.replace(/min/i, String(min)).replace(/max/i, String(max)).split("|").map(r => { + const [minStr, maxStr] = r.split('..'); + const minValue = Number(minStr); + const maxValue = Number(maxStr); + + if (minValue > min) min = minValue; + if (maxValue < max) max = maxValue; + + return { + min: minValue, + max: maxValue + }; + }); + return { + min: min, + max: max, + expression: range && range.length === 1 + ? range[0] + : range && range.length > 1 + ? { operation: "OR", arguments: range } + : undefined + } + }; + + const extractPattern = (): Expression<RegExp> | undefined => { + const pattern = this.extractNodes(this.extractNodes(cur, "type")[0]!, "pattern").map(p => p.arg!).filter(p => !!p).map(p => `^${p}$`); + return pattern && pattern.length == 1 + ? new RegExp(pattern[0]) + : pattern && pattern.length > 1 + ? { operation: "AND", arguments: pattern.map(p => new RegExp(p)) } + : undefined; + } + const mandatory = this.extractValue(cur, "mandatory") === "true" || false; if (!cur.arg) { @@ -729,7 +875,7 @@ export class YangParser { } const element: ViewElementBase = { - id: parentId === 0 ? `${module.name}:${cur.arg}`: cur.arg, + id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg, label: cur.arg, config: config, mandatory: mandatory, @@ -739,10 +885,12 @@ export class YangParser { }; if (type === "string") { + const length = extractRange(0, +18446744073709551615, "length"); return ({ ...element, uiType: "string", - pattern: this.extractNodes(this.extractNodes(cur, "type")[0]!, "pattern").map(p => p.arg!).filter(p => !!p), + length: length.expression, + pattern: extractPattern(), }); } else if (type === "boolean") { return ({ @@ -750,106 +898,109 @@ export class YangParser { uiType: "boolean" }); } else if (type === "uint8") { + const range = extractRange(0, +255); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +255, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "uint16") { + const range = extractRange(0, +65535); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +65535, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "uint32") { + const range = extractRange(0, +4294967295); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +4294967295, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "uint64") { + const range = extractRange(0, +18446744073709551615); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int8") { + const range = extractRange(-128, +127); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : -128, - max: rangeMatch ? Number(rangeMatch[1]) : +127, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int16") { + const range = extractRange(-32768, +32767); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : -32768, - max: rangeMatch ? Number(rangeMatch[1]) : +32767, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int32") { + const range = extractRange(-2147483648, +2147483647); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : -2147483648, - max: rangeMatch ? Number(rangeMatch[1]) : +2147483647, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int64") { + const range = extractRange(-9223372036854775808, +9223372036854775807); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, - }); - } else if (type === "decimal16") { - return ({ - ...element, - uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, - fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 - }); - } else if (type === "decimal32") { - return ({ - ...element, - uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, - fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 }); } else if (type === "decimal64") { + // decimalRange + const fDigits = Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1; + if (fDigits === -1) { + throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found decimal64 with invalid fraction-digits.`); + } + const range = extractRange(YangParser.decimalRange[fDigits].min, YangParser.decimalRange[fDigits].max); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + fDigits: fDigits, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, - fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 }); } else if (type === "enumeration") { const typeNode = this.extractNodes(cur, "type")[0]!; @@ -881,7 +1032,7 @@ export class YangParser { } const refPath = this.resolveReferencePath(vPath, module); const resolve = this.resolveReference.bind(this); - const res : ViewElement = { + const res: ViewElement = { ...element, uiType: "reference", referencePath: refPath, @@ -912,7 +1063,7 @@ export class YangParser { options: [] }; this._identityToResolve.push(() => { - const identity : Identity = this.resolveIdentity(base, module); + const identity: Identity = this.resolveIdentity(base, module); if (!identity) { throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Could not resolve identity [${base}].`); } @@ -925,7 +1076,7 @@ export class YangParser { description: val.description })); }); - return res ; + return res; } else if (type === "empty") { // todo: ❗ handle empty ⚡ /* 9.11. The empty Built-In Type @@ -939,18 +1090,41 @@ export class YangParser { } else if (type === "union") { // todo: ❗ handle union ⚡ /* 9.12. The union Built-In Type */ - console.warn(`found type: union in [${module.name}][${currentPath}][${element.label}]`); - return { + const typeNode = this.extractNodes(cur, "type")[0]!; + const typeNodes = this.extractNodes(typeNode, "type"); + + const resultingElement = { ...element, - uiType: "string", + uiType: "union", + elements: [] + } as ViewElementUnion; + + const resolveUnion = () => { + resultingElement.elements.push(...typeNodes.map(node => { + const stm: Statement = { + ...cur, + sub: [ + ...(cur.sub ?.filter(s => s.key !== "type") || []), + node + ] + }; + return { + ...this.getViewElement(stm, module, parentId, currentPath, isList), + id: node.arg! + }; + })); }; + + this._unionsToResolve.push(resolveUnion); + + return resultingElement; } else if (type === "bits") { const typeNode = this.extractNodes(cur, "type")[0]!; const bitNodes = this.extractNodes(typeNode, "bit"); return { ...element, uiType: "bits", - flags: bitNodes.reduce<{[name: string]: number | undefined; }>((acc, bitNode) => { + flags: bitNodes.reduce<{ [name: string]: number | undefined; }>((acc, bitNode) => { if (!bitNode.arg) { throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found bit without name.`); } @@ -961,25 +1135,60 @@ export class YangParser { }, {}) }; } else if (type === "binary") { - const typeNode = this.extractNodes(cur, "type")[0]!; - const length = Number(this.extractValue(typeNode, "length")); return { ...element, uiType: "binary", - length: length === length ? length : undefined + length: extractRange(0, +18446744073709551615, "length"), }; } else { // not a build in type, have to resolve type - const typeRef = this.resolveType(type, module); + let typeRef = this.resolveType(type, module); if (typeRef == null) console.error(new Error(`Could not resolve type ${type} in [${module.name}][${currentPath}].`)); + + if (isViewElementString(typeRef)) { + typeRef = this.resolveStringType(typeRef, extractPattern(), extractRange(0, +18446744073709551615)); + + } else if (isViewElementNumber(typeRef)) { + typeRef = this.resolveNumberType(typeRef, extractRange(typeRef.min, typeRef.max)); + } + return ({ ...typeRef, ...element, - description: description + description: description, }) as ViewElement; } } + private resolveStringType(parentElement: ViewElementString, pattern: Expression<RegExp> | undefined, length: { expression: Expression<YangRange> | undefined, min: number, max: number }) { + return { + ...parentElement, + pattern: pattern != null && parentElement.pattern + ? { operation: "AND", arguments: [pattern, parentElement.pattern] } + : parentElement.pattern + ? parentElement.pattern + : pattern, + length: length.expression != null && parentElement.length + ? { operation: "AND", arguments: [length.expression, parentElement.length] } + : parentElement.length + ? parentElement.length + : length ?.expression, + } as ViewElementString; + } + + private resolveNumberType(parentElement: ViewElementNumber, range: { expression: Expression<YangRange> | undefined, min: number, max: number }) { + return { + ...parentElement, + range: range.expression != null && parentElement.range + ? { operation: "AND", arguments: [range.expression, parentElement.range] } + : parentElement.range + ? parentElement.range + : range, + min: range.min, + max: range.max, + } as ViewElementNumber; + } + private resolveReferencePath(vPath: string, module: Module) { const vPathParser = /(?:(?:([^\/\:]+):)?([^\/]+))/g // 1 = opt: namespace / 2 = property return vPath.replace(vPathParser, (_, ns, property) => { @@ -988,10 +1197,9 @@ export class YangParser { }); } - private resolveReference(vPath: string, currentPath: string) { const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath - let element : ViewElement | null = null; + let element: ViewElement | null = null; let moduleName = ""; const vPathParts = splitVPath(vPath, vPathParser).map(p => ({ ns: p[1], property: p[2], ind: p[3] })); @@ -999,7 +1207,7 @@ export class YangParser { ? splitVPath(currentPath, vPathParser).map(p => ({ ns: p[1], property: p[2], ind: p[3] })) : []; - for (let i = 0; i < vPathParts.length; ++i){ + for (let i = 0; i < vPathParts.length; ++i) { const vPathPart = vPathParts[i]; if (vPathPart.property === "..") { resultPathParts.pop(); @@ -1009,26 +1217,26 @@ export class YangParser { } // resolve element by path - for (let j = 0; j < resultPathParts.length;++j){ + for (let j = 0; j < resultPathParts.length; ++j) { const pathPart = resultPathParts[j]; - if (j===0) { + if (j === 0) { + moduleName = pathPart.ns; + const rootModule = this._modules[moduleName]; + if (!rootModule) throw new Error("Could not resolve module [" + moduleName + "].\r\n" + vPath); + element = rootModule.elements[`${pathPart.ns}:${pathPart.property}`]; + } else if (element && isViewElementObjectOrList(element)) { + const view: ViewSpecification = this._views[+element.viewId]; + if (moduleName !== pathPart.ns) { moduleName = pathPart.ns; - const rootModule = this._modules[moduleName]; - if (!rootModule) throw new Error("Could not resolve module [" + moduleName +"].\r\n" + vPath); - element = rootModule.elements[`${pathPart.ns}:${pathPart.property}`]; - } else if (element && isViewElementObjectOrList(element)) { - const view: ViewSpecification = this._views[+element.viewId]; - if (moduleName !== pathPart.ns) { - moduleName = pathPart.ns; - element = view.elements[`${moduleName}:${pathPart.property}`]; - } else { - element = view.elements[pathPart.property] || view.elements[`${moduleName}:${pathPart.property}`]; - } + element = view.elements[`${moduleName}:${pathPart.property}`]; } else { - throw new Error("Could not resolve reference.\r\n" + vPath); + element = view.elements[pathPart.property] || view.elements[`${moduleName}:${pathPart.property}`]; } - if (!element) throw new Error("Could not resolve path [" + pathPart.property + "] in ["+ currentPath +"] \r\n" + vPath); + } else { + throw new Error("Could not resolve reference.\r\n" + vPath); } + if (!element) throw new Error("Could not resolve path [" + pathPart.property + "] in [" + currentPath + "] \r\n" + vPath); + } return element; } diff --git a/sdnr/wt/odlux/apps/configurationApp/tsconfig.json b/sdnr/wt/odlux/apps/configurationApp/tsconfig.json index b0c9b424d..c95005660 100644 --- a/sdnr/wt/odlux/apps/configurationApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/configurationApp/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/apps/connectApp/pom.xml b/sdnr/wt/odlux/apps/connectApp/pom.xml index cfc7720c8..619c02a2d 100644 --- a/sdnr/wt/odlux/apps/connectApp/pom.xml +++ b/sdnr/wt/odlux/apps/connectApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-connectApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts index bf4778b5b..a6d81c10c 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts @@ -23,7 +23,7 @@ import { Action } from '../../../../framework/src/flux/action'; import { Dispatch } from '../../../../framework/src/flux/store'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { networkElementsReloadAction } from '../handlers/networkElementsHandler'; +import { networkElementsReloadAction, networkElementsReloadActionAsync } from '../handlers/networkElementsHandler'; import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler'; import { PanelId } from '../models/panelId'; @@ -38,7 +38,7 @@ export class SetPanelAction extends Action { } export class AddWebUriList extends Action { - constructor(public element: guiCutThrough[], public knownElements: string[]) { + constructor(public searchedElements: guiCutThrough[], public notSearchedElements: string[], public newlySearchedElements?: string[]) { super(); } } @@ -53,58 +53,65 @@ export const removeWebUriAction = (nodeId: string) => { return new RemoveWebUri(nodeId); } +export class SetWeburiSearchBusy extends Action { + constructor(public isbusy: boolean) { + super(); + } +} + let isBusy = false; -export const findWebUrisForGuiCutThroughAsyncAction = (dispatcher: Dispatch) => (networkElements: NetworkElementConnection[], knownElements: string[]) => { +export const findWebUrisForGuiCutThroughAsyncAction = (networkElements: NetworkElementConnection[]) => async (dispatcher: Dispatch, getState: () => IApplicationStoreState) => { // keep method from executing simultanously; state not used because change of iu isn't needed if (isBusy) return; isBusy = true; - const nodeIds = networkElements.map(element => { return element.id as string }); + const { connect: { guiCutThrough } } = getState(); - if (knownElements.length > 0) { + let notConnectedElements: string[] = []; + let elementsToSearch: string[] = []; + let prevFoundElements: string[] = []; - let elementsToSearch: string[] = []; - nodeIds.forEach(element => { - // find index of nodeId - const index = knownElements.indexOf(element); + networkElements.forEach(item => { + const id = item.id as string; + if (item.status === "Connected") { - // if element dosen't exist, add it to list - if (index === -1) { - elementsToSearch.push(element) + // element is connected and is added to search list, if it doesn't exist already + const exists = guiCutThrough.searchedElements.filter(element => element.nodeId === id).length > 0; + if (!exists) { + elementsToSearch.push(id); + + //element was found previously, but not searched for a weburi + if (guiCutThrough.notSearchedElements.length > 0 && guiCutThrough.notSearchedElements.includes(id)) { + prevFoundElements.push(id); + } + } + } + else { + // element isn't connected and cannot be searched for a weburi + if (!guiCutThrough.notSearchedElements.includes(id)) { + notConnectedElements.push(item.id as string); } - }); - - // if new elements were found, search for weburi - if (elementsToSearch.length > 0) { - const foundWebUris = connectService.getAllWebUriExtensionsForNetworkElementListAsync(elementsToSearch); - foundWebUris.then(result => { - dispatcher(new AddWebUriList(result, elementsToSearch)); - isBusy = false; - }) - - } else { - isBusy = false; } + }); - } else { - connectService.getAllWebUriExtensionsForNetworkElementListAsync(nodeIds).then(result => { - dispatcher(new AddWebUriList(result, nodeIds)); - isBusy = false; - }) + if (elementsToSearch.length > 0 || notConnectedElements.length > 0) { + const result = await connectService.getAllWebUriExtensionsForNetworkElementListAsync(elementsToSearch); + dispatcher(new AddWebUriList(result, notConnectedElements, prevFoundElements)); } + isBusy = false; } export const setPanelAction = (panelId: PanelId) => { return new SetPanelAction(panelId); } -export const updateCurrentViewAsyncAction = () => (dispatch: Dispatch, getState: () => IApplicationStoreState) => { +export const updateCurrentViewAsyncAction = () => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { const { connect: { currentOpenPanel } } = getState(); if (currentOpenPanel === "NetworkElements") { - return dispatch(networkElementsReloadAction); + return await dispatch(networkElementsReloadActionAsync); } else { return dispatch(connectionStatusLogReloadAction); diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts index 1a86f94b1..1e569a196 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts @@ -43,7 +43,7 @@ export const editNetworkElementAsyncActionCreator = (element: UpdateNetworkEleme const res = await connectService.deleteNetworkElement(element); } else { - const res = await connectService.updateNetworkElement(element); + const res = await connectService.updateNetworkElement(element); } dispatch(updateCurrentViewAsyncAction()); dispatch(new AddSnackbarNotification({ message: `Successfully modified [${element.id}]`, options: { variant: 'success' } })); @@ -54,7 +54,7 @@ export const editNetworkElementAsyncActionCreator = (element: UpdateNetworkEleme export const removeNetworkElementAsyncActionCreator = (element: UpdateNetworkElement) => async (dispatch: Dispatch) => { const res = await connectService.deleteNetworkElement(element); await dispatch(unmountNetworkElementAsyncActionCreator(element && element.id)); - dispatch(updateCurrentViewAsyncAction()); + await dispatch(updateCurrentViewAsyncAction()); }; diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx index 1e1f11523..ce7f48cc9 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx @@ -60,8 +60,8 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element)); await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(mountElement)); }, - removeNetworkElement: (element: UpdateNetworkElement) => { - dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element)); + removeNetworkElement: async (element: UpdateNetworkElement) => { + await dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element)); dispatcher.dispatch(removeWebUriAction(element.id)); } }); @@ -158,7 +158,7 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme const setting = settings[this.props.mode]; return ( <Dialog open={this.props.mode !== EditNetworkElementDialogMode.None}> - <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle> + <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle> <DialogContent> <DialogContentText> {setting.dialogDescription} @@ -179,7 +179,7 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme </FormControl> </DialogContent> <DialogActions> - <Button onClick={(event) => { + <Button aria-label="dialog-confirm-button" onClick={(event) => { this.onApply({ isRequired: this.state.isRequired, id: this.state.nodeId, @@ -192,7 +192,7 @@ class EditNetworkElementDialogComponent extends React.Component<EditNetworkEleme event.preventDefault(); event.stopPropagation(); }} > {setting.applyButtonText} </Button> - <Button onClick={(event) => { + <Button aria-label="dialog-cancel-button" onClick={(event) => { this.onCancel(); event.preventDefault(); event.stopPropagation(); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts index 1440599fd..23a801424 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts @@ -21,7 +21,7 @@ import { combineActionHandler } from '../../../../framework/src/flux/middleware' import { INetworkElementsState, networkElementsActionHandler } from './networkElementsHandler'; import { IConnectionStatusLogState, connectionStatusLogActionHandler } from './connectionStatusLogHandler'; import { IInfoNetworkElementsState, infoNetworkElementsActionHandler } from './infoNetworkElementHandler'; -import { SetPanelAction, AddWebUriList, RemoveWebUri } from '../actions/commonNetworkElementsActions'; +import { SetPanelAction, AddWebUriList, RemoveWebUri, SetWeburiSearchBusy } from '../actions/commonNetworkElementsActions'; import { PanelId } from '../models/panelId'; import { guiCutThrough } from '../models/guiCutTrough'; @@ -41,26 +41,33 @@ const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action) } interface guiCutThroughState { - availableWebUris: guiCutThrough[]; - knownElements: string[]; + searchedElements: guiCutThrough[]; + notSearchedElements: string[]; } -const guiCutThroughHandler: IActionHandler<guiCutThroughState> = (state = { availableWebUris: [], knownElements: [] }, action) => { +const guiCutThroughHandler: IActionHandler<guiCutThroughState> = (state = { searchedElements: [], notSearchedElements: [] }, action) => { if (action instanceof AddWebUriList) { - let knownElements: string[]; - let availableWebUris: guiCutThrough[]; + let notSearchedElements: string[]; + let searchedElements: guiCutThrough[]; - knownElements = state.knownElements.concat(action.knownElements); + notSearchedElements = state.notSearchedElements.concat(action.notSearchedElements); - availableWebUris = state.availableWebUris.concat(action.element); + //remove elements, which were just searched + if (action.newlySearchedElements) { + action.newlySearchedElements.forEach(item => { + notSearchedElements = notSearchedElements.filter(id => id !== item); + }) + } - state = { availableWebUris: availableWebUris, knownElements: knownElements } + searchedElements = state.searchedElements.concat(action.searchedElements); + + state = { searchedElements: searchedElements, notSearchedElements: notSearchedElements } } else if (action instanceof RemoveWebUri) { const nodeId = action.element; - const webUris = state.availableWebUris.filter(item => item.nodeId !== nodeId); - const knownElements = state.knownElements.filter(item => item !== nodeId); - state = { knownElements: knownElements, availableWebUris: webUris }; + const webUris = state.searchedElements.filter(item => item.nodeId !== nodeId); + const knownElements = state.notSearchedElements.filter(item => item !== nodeId); + state = { notSearchedElements: knownElements, searchedElements: webUris }; } return state; } diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts index 78c7000d2..4fe858c69 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts @@ -31,11 +31,13 @@ export const { createActions: createNetworkElementsActions, createProperties: createNetworkElementsProperties, reloadAction: networkElementsReloadAction, + reloadActionAsync: networkElementsReloadActionAsync // set value action, to change a value } = createExternal<NetworkElementConnection>(networkElementsSearchHandler, appState => { - const webUris = appState.connect.guiCutThrough.availableWebUris; + const webUris = appState.connect.guiCutThrough.searchedElements; + // add weburi links, if element is connected & weburi available if (appState.connect.networkElements.rows && webUris.length > 0) { appState.connect.networkElements.rows.forEach(element => { diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts b/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts index 4b443bac8..d44eea685 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts @@ -1 +1,19 @@ -export type guiCutThrough = { webUri: string, nodeId: string }
\ No newline at end of file +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +export type guiCutThrough = { webUri?: string, nodeId: string }
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts index 4c2dc9b09..f58dc58ab 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts @@ -1,3 +1,21 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + export type NetworkElementConnection = { id?: string; nodeId: string; diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts index b51412055..2000d9407 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts @@ -1 +1,19 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + export type PanelId = null | "NetworkElements" | "ConnectionStatusLog";
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts index 7a410f4ae..41449e83f 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts @@ -188,14 +188,20 @@ class ConnectService { promises.push(requestRest<any>(path, { method: "GET" }) .then(result => { - if (result['network-element'] && result['network-element'].extension) { + if (result != null && result['network-element'] && result['network-element'].extension) { const webUri = result['network-element'].extension.find((item: any) => item['value-name'] === "webUri") if (webUri) { webUris.push({ webUri: webUri.value, nodeId: nodeId }); + } else { + webUris.push({ webUri: undefined, nodeId: nodeId }); } + } else { + webUris.push({ webUri: undefined, nodeId: nodeId }); } }) - .catch(error => console.log("network element is unreachable: " + error))) + .catch(error => { + webUris.push({ webUri: undefined, nodeId: nodeId }); + })) }) diff --git a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx index aa3391c47..f8c0f3a82 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx @@ -25,7 +25,7 @@ import { connectionStatusLogReloadAction, createConnectionStatusLogActions } fro import { NetworkElementsList } from '../components/networkElements'; import { ConnectionStatusLog } from '../components/connectionStatusLog'; -import { setPanelAction, findWebUrisForGuiCutThroughAsyncAction } from '../actions/commonNetworkElementsActions'; +import { setPanelAction, findWebUrisForGuiCutThroughAsyncAction, SetWeburiSearchBusy } from '../actions/commonNetworkElementsActions'; import { PanelId } from '../models/panelId'; import { NetworkElementConnection } from 'models/networkElementConnection'; @@ -42,7 +42,8 @@ const mapDispatcher = (dispatcher: IDispatcher) => ({ onLoadNetworkElements: () => { dispatcher.dispatch(networkElementsReloadAction); }, - loadWebUris: findWebUrisForGuiCutThroughAsyncAction(dispatcher.dispatch), + loadWebUris: async (networkElements: NetworkElementConnection[]) => { await dispatcher.dispatch(findWebUrisForGuiCutThroughAsyncAction(networkElements)) }, + isBusy: (busy: boolean) => dispatcher.dispatch(new SetWeburiSearchBusy(busy)), onLoadConnectionStatusLog: () => { dispatcher.dispatch(connectionStatusLogReloadAction); }, @@ -55,13 +56,12 @@ type ConnectApplicationComponentProps = Connect<typeof mapProps, typeof mapDispa class ConnectApplicationComponent extends React.Component<ConnectApplicationComponentProps>{ - componentDidUpdate = () => { + componentDidUpdate = async () => { // search for guicutthroughs after networkelements were found const networkElements = this.props.netWorkElements; - const guiCuttrough = this.props.availableGuiCutroughs; - if (networkElements.rows.length > 0 && networkElements.total !== guiCuttrough.knownElements.length) { - this.props.loadWebUris(networkElements.rows, guiCuttrough.knownElements); + if (networkElements.rows.length > 0) { + await this.props.loadWebUris(networkElements.rows); } } diff --git a/sdnr/wt/odlux/apps/connectApp/webpack.config.js b/sdnr/wt/odlux/apps/connectApp/webpack.config.js index 6d70b04b0..aa3acf00c 100644 --- a/sdnr/wt/odlux/apps/connectApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/connectApp/webpack.config.js @@ -126,23 +126,23 @@ module.exports = (env) => { }, proxy: { "/oauth2/": { - target: "http://10.20.6.29:28181", + target: "http://10.20.6.29:48181", secure: false }, "/database/": { - target: "http://10.20.6.29:28181", + target: "http://10.20.6.29:48181", secure: false }, "/restconf/": { - target: "http://10.20.6.29:28181", + target: "http://10.20.6.29:48181", secure: false }, "/help/": { - target: "http://10.20.6.29:28181", + target: "http://10.20.6.29:48181", secure: false }, "/websocket": { - target: "http://10.20.6.29:28181", + target: "http://10.20.6.29:48181", ws: true, changeOrigin: true, secure: false diff --git a/sdnr/wt/odlux/apps/demoApp/pom.xml b/sdnr/wt/odlux/apps/demoApp/pom.xml index 92ef6856a..9e96cf410 100644 --- a/sdnr/wt/odlux/apps/demoApp/pom.xml +++ b/sdnr/wt/odlux/apps/demoApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-demoApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/eventLogApp/pom.xml b/sdnr/wt/odlux/apps/eventLogApp/pom.xml index d84a10664..e5ac653d5 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/pom.xml +++ b/sdnr/wt/odlux/apps/eventLogApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-eventLogApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/faultApp/pom.xml b/sdnr/wt/odlux/apps/faultApp/pom.xml index 119d636bf..63825dd54 100644 --- a/sdnr/wt/odlux/apps/faultApp/pom.xml +++ b/sdnr/wt/odlux/apps/faultApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-faultApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/helpApp/pom.xml b/sdnr/wt/odlux/apps/helpApp/pom.xml index b40de2f08..760049cac 100644 --- a/sdnr/wt/odlux/apps/helpApp/pom.xml +++ b/sdnr/wt/odlux/apps/helpApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-helpApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/inventoryApp/pom.xml b/sdnr/wt/odlux/apps/inventoryApp/pom.xml index aad75caf9..9420b6cfa 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/pom.xml +++ b/sdnr/wt/odlux/apps/inventoryApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-inventoryApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml index 0d51219e9..0196e3929 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml +++ b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-maintenanceApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/mediatorApp/pom.xml b/sdnr/wt/odlux/apps/mediatorApp/pom.xml index 038761afe..5a3b8023b 100644 --- a/sdnr/wt/odlux/apps/mediatorApp/pom.xml +++ b/sdnr/wt/odlux/apps/mediatorApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-mediatorApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/minimumApp/pom.xml b/sdnr/wt/odlux/apps/minimumApp/pom.xml index f1eb06abf..81af1fbe5 100644 --- a/sdnr/wt/odlux/apps/minimumApp/pom.xml +++ b/sdnr/wt/odlux/apps/minimumApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-minimumApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml index 596872126..e60a6c1c2 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml @@ -14,7 +14,7 @@ <version>0.7.1-SNAPSHOT</version> <packaging>bundle</packaging> - <name>ccsdk-features :: ${project.artifactId}</name> + <name>sdnr-wt-odlux-app-performanceHistoryApp</name> <licenses> <license> <name>Apache License, Version 2.0</name> @@ -103,10 +103,10 @@ </executions> </plugin> <plugin> - <groupId>de.jacks-it-lab</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.2</version> - <executions> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> <execution> <id>install node and yarn</id> <goals> |