1 import { Action } from '../../../../framework/src/flux/action';
2 import { Dispatch } from '../../../../framework/src/flux/store';
3 import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
4 import { PushAction, ReplaceAction } from "../../../../framework/src/actions/navigationActions";
5 import { AddErrorInfoAction } from "../../../../framework/src/actions/errorActions";
7 import { DisplayModeType, DisplaySpecification } from '../handlers/viewDescriptionHandler';
8 import { restService } from "../services/restServices";
9 import { YangParser } from "../yang/yangParser";
10 import { Module } from "../models/yang";
11 import { ViewSpecification, ViewElement, isViewElementReference, isViewElementList, isViewElementObjectOrList, isViewElementRpc, isViewElementChoise, ViewElementChoiseCase, ViewElementString } from "../models/uiModels";
12 import { exception } from 'console';
14 export class EnableValueSelector extends Action {
15 constructor(public listSpecification: ViewSpecification, public listData: any[], public keyProperty: string, public onValueSelected : (value: any) => void ) {
20 export class SetCollectingSelectionData extends Action {
21 constructor(public busy: boolean) {
26 export class SetSelectedValue extends Action {
27 constructor(public value: any) {
32 export class UpdateDeviceDescription extends Action {
33 constructor( public nodeId: string, public modules: { [name:string]: Module}, public views: ViewSpecification[]) {
38 export class UpdatViewDescription extends Action {
39 constructor (public vPath: string, public viewData: any, public displaySpecification: DisplaySpecification = { displayMode: DisplayModeType.doNotDisplay }) {
44 export class UpdatOutputData extends Action {
45 constructor (public outputData: any) {
50 export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => {
52 dispatch(new UpdateDeviceDescription("", {}, []));
53 dispatch(new SetCollectingSelectionData(true));
55 const { avaliableCapabilities, unavaliableCapabilities } = await restService.getCapabilitiesByMoutId(nodeId);
57 if (!avaliableCapabilities || avaliableCapabilities.length <= 0) {
58 throw new Error(`NetworkElement : [${nodeId}] has no capabilities.`);
61 const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i;
63 const parser = new YangParser(unavaliableCapabilities?.map(cap => {
64 const capMatch = cap && capParser.exec(cap.capability);
65 return { capability:capMatch && capMatch[2] || '', failureReason: cap.failureReason };
68 for (let i = 0; i < avaliableCapabilities.length; ++i){
69 const capRaw = avaliableCapabilities[i];
70 const capMatch = capRaw && capParser.exec(capRaw.capability);
72 capMatch && await parser.addCapability(capMatch[2], capMatch[1]);
74 console.error(`Error in ${capMatch && capMatch[2]} ${capMatch && capMatch[1]}`, err);
80 dispatch(new SetCollectingSelectionData(false));
82 if (process.env.NODE_ENV === "development" ) {
83 console.log(parser, parser.modules, parser.views);
86 return dispatch(new UpdateDeviceDescription(nodeId, parser.modules, parser.views));
89 export const splitVPath = (vPath: string, vPathParser : RegExp): [string, string?][] => {
90 const pathParts: [string, string?][] = [];
91 let partMatch: RegExpExecArray | null;
93 partMatch = vPathParser.exec(vPath);
95 pathParts.push([partMatch[1], partMatch[2] || undefined]);
101 const getReferencedDataList = async (refPath: string, dataPath: string, modules: { [name: string]: Module }, views: ViewSpecification[]) => {
102 const pathParts = splitVPath(refPath, /(?:(?:([^\/\:]+):)?([^\/]+))/g); // 1 = opt: namespace / 2 = property
103 const defaultNS = pathParts[0][0];
104 let referencedModule = modules[defaultNS];
106 let dataMember: string;
107 let view: ViewSpecification;
108 let currentNS: string | null = null;
109 let dataUrls = [dataPath];
112 for (let i = 0; i < pathParts.length; ++i) {
113 const [pathPartNS, pathPart] = pathParts[i];
114 const namespace = pathPartNS != null ? (currentNS = pathPartNS) : currentNS;
116 const viewElement = i === 0
117 ? views[0].elements[`${referencedModule.name}:${pathPart}`]
118 : view!.elements[`${pathPart}`] || view!.elements[`${namespace}:${pathPart}`];
120 if (!viewElement) throw new Error(`Could not find ${pathPart} in ${refPath}`);
121 if (i < pathParts.length - 1) {
122 if (!isViewElementObjectOrList(viewElement)) {
123 throw Error(`Module: [${referencedModule.name}].[${viewElement.label}]. Viewelement is not list or object.`);
125 view = views[+viewElement.viewId];
126 const resultingDataUrls : string[] = [];
127 if (isViewElementList(viewElement)) {
128 for (let j = 0; j < dataUrls.length; ++j) {
129 const dataUrl = dataUrls[j];
130 const restResult = (await restService.getConfigData(dataUrl));
131 if (restResult.data == null || restResult.status < 200 || restResult.status > 299) {
132 const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
133 throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
136 let dataRaw = restResult.data[`${defaultNS}:${dataMember!}`];
137 if (dataRaw === undefined) {
138 dataRaw = restResult.data[dataMember!];
140 dataRaw = dataRaw instanceof Array
144 data = dataRaw && dataRaw[viewElement.label] || [];
145 const keys: string[] = data.map((entry: { [key: string]: any } )=> entry[viewElement.key!]);
146 resultingDataUrls.push(...keys.map(key => `${dataUrl}/${viewElement.label.replace(/\//ig, "%2F")}=${key.replace(/\//ig, "%2F")}`));
148 dataMember = viewElement.label;
150 // just a member, not a list
151 const pathSegment = (i === 0
152 ? `/${referencedModule.name}:${viewElement.label.replace(/\//ig, "%2F")}`
153 : `/${viewElement.label.replace(/\//ig, "%2F")}`);
154 resultingDataUrls.push(...dataUrls.map(dataUrl => dataUrl + pathSegment));
155 dataMember = viewElement.label;
157 dataUrls = resultingDataUrls;
160 for (let j = 0; j < dataUrls.length; ++j) {
161 const dataUrl = dataUrls[j];
162 const restResult = (await restService.getConfigData(dataUrl));
163 if (restResult.data == null || restResult.status < 200 || restResult.status > 299) {
164 const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
165 throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
167 let dataRaw = restResult.data[`${defaultNS}:${dataMember!}`];
168 if (dataRaw === undefined) {
169 dataRaw = restResult.data[dataMember!];
171 dataRaw = dataRaw instanceof Array
176 // BUG UUID ist nicht in den elements enthalten !!!!!!
177 const key = viewElement && viewElement.label || pathPart;
188 const resolveViewDescription = (defaultNS: string | null, vPath: string, view: ViewSpecification): ViewSpecification =>{
190 // check if-feature | when | and resolve all references.
192 view.elements = Object.keys(view.elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => {
193 const resolveHistory : ViewElement[] = [];
194 let elm = view.elements[cur];
195 const key = defaultNS && cur.replace(new RegExp(`^${defaultNS}:`, "i"),"") || cur;
196 while (isViewElementReference(elm)) {
197 const result = (elm.ref(vPath));
199 const [referencedElement, referencedPath] = result;
200 if (resolveHistory.some(hist => hist === referencedElement)) {
201 console.error(`Circle reference found at: ${vPath}`, resolveHistory);
204 elm = referencedElement;
205 vPath = referencedPath;
206 resolveHistory.push(elm);
210 acc[key] = { ...elm, id: key };
217 const flatenViewElements = (defaultNS: string | null, parentPath: string, elements: { [name: string]: ViewElement }, views: ViewSpecification[], currentPath: string ): { [name: string]: ViewElement } => {
218 if (!elements) return {};
219 return Object.keys(elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => {
220 const elm = elements[cur];
222 // remove the detault namespace, and only the default namespace, sine it seems that this is also not in the restconf response
223 const elmKey = defaultNS && elm.id.replace(new RegExp(`^${defaultNS}:`, "i"), "") || elm.id;
224 const key = parentPath ? `${parentPath}.${elmKey}` : elmKey;
226 if (isViewElementRpc(elm)) {
227 console.warn(`Flaten of RFC not supported ! [${currentPath}][${elm.label}]`);
229 } else if (isViewElementObjectOrList(elm)) {
230 const view = views[+elm.viewId];
231 const inner = view && flatenViewElements(defaultNS, key, view.elements, views, `${currentPath}/${view.name}`);
232 inner && Object.keys(inner).forEach(k => (acc[k] = inner[k]));
233 } else if (isViewElementChoise(elm)) {
237 cases: Object.keys(elm.cases).reduce<{ [name: string]: ViewElementChoiseCase }>((accCases, curCases) => {
238 const caseElement = elm.cases[curCases];
239 accCases[curCases] = {
241 // Hint: do not use key it contains elmKey, which shell be omitted for cases.
242 elements: flatenViewElements(defaultNS, /*key*/ parentPath, caseElement.elements, views, `${currentPath}/${elm.label}`)
257 export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
258 const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
259 const { configuration: { deviceDescription: { nodeId, modules, views } }, framework: { navigationState } } = getState();
260 let dataPath = `/rests/data/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`;
262 let inputViewSpecification: ViewSpecification | undefined = undefined;
263 let outputViewSpecification: ViewSpecification | undefined = undefined;
265 let viewSpecification: ViewSpecification = views[0];
266 let viewElement: ViewElement;
268 let dataMember: string;
269 let extractList: boolean = false;
271 let currentNS: string | null = null;
272 let defaultNS: string | null = null;
274 dispatch(new SetCollectingSelectionData(true));
276 for (let ind = 0; ind < pathParts.length; ++ind) {
277 const [property, key] = pathParts[ind];
278 const namespaceInd = property && property.indexOf(":") || -1;
279 const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
281 if (ind === 0) { defaultNS = namespace };
283 viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
284 if (!viewElement) throw Error("Property [" + property + "] does not exist.");
286 if (viewElement.isList && !key) {
287 if (pathParts.length - 1 > ind) {
288 dispatch(new SetCollectingSelectionData(false));
289 throw new Error("No key for list [" + property + "]");
290 } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
292 // empty key is used for new element
293 if (viewElement && "viewId" in viewElement) viewSpecification = views[+viewElement.viewId];
294 const data = Object.keys(viewSpecification.elements).reduce<{ [name: string]: any }>((acc, cur) => {
295 const elm = viewSpecification.elements[cur];
297 acc[elm.id] = elm.default || ""
302 // create display specification
303 const ds: DisplaySpecification = {
304 displayMode: DisplayModeType.displayAsObject,
305 viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification),
306 keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined
309 // update display specification
310 return dispatch(new UpdatViewDescription(vPath, data, ds));
312 if (viewElement && isViewElementList(viewElement) && viewSpecification.parentView === "0") {
313 // check if there is a reference as key
314 const listSpecification = views[+viewElement.viewId];
315 const keyElement = viewElement.key && listSpecification.elements[viewElement.key];
316 if (keyElement && isViewElementReference(keyElement)) {
317 const refList = await getReferencedDataList(keyElement.referencePath, dataPath, modules, views);
319 throw new Error(`Could not find refList for [${keyElement.referencePath}].`);
322 throw new Error(`Key property not found for [${keyElement.referencePath}].`);
324 dispatch(new EnableValueSelector(refList.view, refList.data, refList.key, (refKey) => {
325 window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, "%2F")}]`)));
328 // Found a list at root level of a module w/o a refenrece key.
329 dataPath += `?content=config&fields=${encodeURIComponent(viewElement.id)}(${encodeURIComponent(viewElement.key || '')})`;
330 const restResult = (await restService.getConfigData(dataPath));
331 if (restResult && restResult.status === 200 && restResult.data && restResult.data[viewElement.id] ){
332 // spoof the not existing view here
333 const refData = restResult.data[viewElement.id];
334 const refView : ViewSpecification = {
340 [viewElement.key!] : {
344 label: viewElement.key,
346 } as ViewElementString
349 dispatch(new EnableValueSelector(refView, refData, viewElement.key!, (refKey) => {
350 window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, "%2F")}]`)));
353 throw new Error("Found a list at root level of a module and could not determine the keys.");
355 dispatch(new SetCollectingSelectionData(false));
362 dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`;
364 // in case of the root element the required namespace will be added later,
365 // while extracting the data
366 dataMember = namespace === defaultNS
368 : `${namespace}:${viewElement.label}`;
372 if (viewElement && "viewId" in viewElement) {
373 viewSpecification = views[+viewElement.viewId];
374 } else if (viewElement.uiType === "rpc") {
375 viewSpecification = views[+(viewElement.inputViewId || 0)];
377 // create new instance & flaten
378 inputViewSpecification = viewElement.inputViewId != null && {
379 ...views[+(viewElement.inputViewId || 0)],
380 elements: flatenViewElements(defaultNS, "", views[+(viewElement.inputViewId || 0)].elements, views, viewElement.label),
382 outputViewSpecification = viewElement.outputViewId != null && {
383 ...views[+(viewElement.outputViewId || 0)],
384 elements: flatenViewElements(defaultNS, "", views[+(viewElement.outputViewId || 0)].elements, views, viewElement.label),
391 // do not get any data from netconf if there is no view specified || this is the root element [0] || this is an rpc
392 if (viewSpecification && !(viewSpecification.id === "0" || viewElement!.uiType === "rpc")) {
393 const restResult = (await restService.getConfigData(dataPath));
394 if (!restResult.data) {
395 // special case: if this is a list without any response
396 if (extractList && restResult.status === 404) {
397 if (!isViewElementList(viewElement!)) {
398 throw new Error(`vPath: [${vPath}]. ViewElement has no key.`);
400 // create display specification
401 const ds: DisplaySpecification = {
402 displayMode: extractList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject,
403 viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification),
404 keyProperty: viewElement.key
407 // update display specification
408 return dispatch(new UpdatViewDescription(vPath, [], ds));
410 throw new Error(`Did not get response from Server. Status: [${restResult.status}]`);
411 } else if (restResult.status < 200 || restResult.status > 299) {
412 const message = restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
413 throw new Error(`Server Error. Status: [${restResult.status}]\n${message}`);
415 // https://tools.ietf.org/html/rfc7951#section-4 the root element may countain a namesapce or not !
416 data = restResult.data[`${defaultNS}:${dataMember!}`];
417 if (data === undefined) {
418 data = restResult.data[dataMember!]; // extract dataMember w/o namespace
422 // extract the first element list[key]
423 data = data instanceof Array
427 // extract the list -> key: list
429 ? data[viewElement!.id] || data[viewElement!.label] || [] // if the list is empty, it does not exist
432 } else if (viewElement! && viewElement!.uiType === "rpc") {
433 // set data to defaults
435 inputViewSpecification && Object.keys(inputViewSpecification.elements).forEach(key => {
436 const elm = inputViewSpecification && inputViewSpecification.elements[key];
437 if (elm && elm.default != undefined) {
438 data[elm.id] = elm.default;
443 // create display specification
444 const ds: DisplaySpecification = viewElement! && viewElement!.uiType === "rpc"
447 displayMode: DisplayModeType.displayAsRPC,
448 inputViewSpecification: inputViewSpecification && resolveViewDescription(defaultNS, vPath, inputViewSpecification),
449 outputViewSpecification: outputViewSpecification && resolveViewDescription(defaultNS, vPath, outputViewSpecification),
453 displayMode: extractList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject,
454 viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification),
455 keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined,
456 apidocPath: isViewElementList(viewElement!) && `/apidoc/explorer/index.html?urls.primaryName=$$$standard$$$#/mounted%20${nodeId}%20${viewElement!.module || 'MODULE_NOT_DEFINED'}/$$$action$$$_${dataPath.replace(/^\//,'').replace(/[\/=\-\:]/g,'_')}_${viewElement! != null ? `${viewElement.id.replace(/[\/=\-\:]/g,'_')}_` : '' }` || undefined,
459 // update display specification
460 return dispatch(new UpdatViewDescription(vPath, data, ds));
461 // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]
462 // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]/lp
465 dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not process ${dataPath}` }));
466 dispatch(new SetCollectingSelectionData(false));
472 export const updateDataActionAsyncCreator = (vPath: string, data: any) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
473 const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
474 const { configuration: { deviceDescription: { nodeId, views } } } = getState();
475 let dataPath = `/rests/data/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`;
476 let viewSpecification: ViewSpecification = views[0];
477 let viewElement: ViewElement;
478 let dataMember: string;
479 let embedList: boolean = false;
480 let isNew: string | false = false;
482 let currentNS: string | null = null;
483 let defaultNS: string | null = null;
485 dispatch(new SetCollectingSelectionData(true));
487 for (let ind = 0; ind < pathParts.length; ++ind) {
488 let [property, key] = pathParts[ind];
489 const namespaceInd = property && property.indexOf(":") || -1;
490 const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
492 if (ind === 0) { defaultNS = namespace };
493 viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
494 if (!viewElement) throw Error("Property [" + property + "] does not exist.");
496 if (isViewElementList(viewElement) && !key) {
498 if (viewElement && viewElement.isList && viewSpecification.parentView === "0") {
499 throw new Error("Found a list at root level of a module w/o a refenrece key.");
501 if (pathParts.length - 1 > ind) {
502 dispatch(new SetCollectingSelectionData(false));
503 throw new Error("No key for list [" + property + "]");
504 } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
505 // handle new element
506 key = viewElement.key && String(data[viewElement.key]) || "";
509 dispatch(new SetCollectingSelectionData(false));
510 throw new Error("No value for key [" + viewElement.key + "] in list [" + property + "]");
515 dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`;
516 dataMember = viewElement.label;
519 if (viewElement && "viewId" in viewElement) {
520 viewSpecification = views[+viewElement.viewId];
524 // remove read-only elements
525 const removeReadOnlyElements = (viewSpecification: ViewSpecification, isList: boolean, data: any) => {
527 return data.map((elm : any) => removeReadOnlyElements(viewSpecification, false, elm));
529 return Object.keys(data).reduce<{[key: string]: any}>((acc, cur)=>{
530 const [nsOrName, name] = cur.split(':',1);
531 const element = viewSpecification.elements[cur] || viewSpecification.elements[nsOrName] || viewSpecification.elements[name];
532 if (!element && process.env.NODE_ENV === "development" ) {
533 throw new Error("removeReadOnlyElements: Could not determine elment for data.");
535 if (element && element.config) {
536 if (element.uiType==="object") {
537 const view = views[+element.viewId];
539 throw new Error("removeReadOnlyElements: Internal Error could not determine viewId: "+element.viewId);
541 acc[cur] = removeReadOnlyElements(view, element.isList != null && element.isList, data[cur]);
543 acc[cur] = data[cur];
550 data = removeReadOnlyElements(viewSpecification, embedList, data);
553 // embed the list -> key: list
555 ? { [viewElement!.label]: data }
558 // embed the first element list[key]
563 // do not extract root member (0)
564 if (viewSpecification && viewSpecification.id !== "0") {
565 const updateResult = await restService.setConfigData(dataPath, { [`${currentNS}:${dataMember!}`]: data }); // addDataMember using currentNS
566 if (updateResult.status < 200 || updateResult.status > 299) {
567 const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || "";
568 throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`);
573 return dispatch(new ReplaceAction(`/configuration/${nodeId}/${vPath.replace(/\[\]$/i, `[${isNew}]`)}`)) // navigate to new element
576 // create display specification
577 const ds: DisplaySpecification = {
578 displayMode: embedList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject,
579 viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification),
580 keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined,
583 // update display specification
584 return dispatch(new UpdatViewDescription(vPath, data, ds));
587 dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` }));
590 dispatch(new SetCollectingSelectionData(false));
595 export const removeElementActionAsyncCreator = (vPath: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
596 const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
597 const { configuration: { deviceDescription: { nodeId, views } } } = getState();
598 let dataPath = `/rests/data/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`;
599 let viewSpecification: ViewSpecification = views[0];
600 let viewElement: ViewElement;
602 let currentNS: string | null = null;
603 let defaultNS: string | null = null;
605 dispatch(new SetCollectingSelectionData(true));
607 for (let ind = 0; ind < pathParts.length; ++ind) {
608 let [property, key] = pathParts[ind];
609 const namespaceInd = property && property.indexOf(":") || -1;
610 const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
612 if (ind === 0) { defaultNS = namespace };
613 viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
614 if (!viewElement) throw Error("Property [" + property + "] does not exist.");
616 if (isViewElementList(viewElement) && !key) {
617 if (viewElement && viewElement.isList && viewSpecification.parentView === "0") {
618 throw new Error("Found a list at root level of a module w/o a refenrece key.");
620 if (pathParts.length - 1 > ind) {
621 dispatch(new SetCollectingSelectionData(false));
622 throw new Error("No key for list [" + property + "]");
623 } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
624 // remove the whole table
628 dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`;
630 if (viewElement && "viewId" in viewElement) {
631 viewSpecification = views[+viewElement.viewId];
632 } else if (viewElement.uiType === "rpc") {
633 viewSpecification = views[+(viewElement.inputViewId || 0)];
637 const updateResult = await restService.removeConfigElement(dataPath);
638 if (updateResult.status < 200 || updateResult.status > 299) {
639 const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || "";
640 throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`);
643 dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not remove ${dataPath}` }));
645 dispatch(new SetCollectingSelectionData(false));
651 export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
652 const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
653 const { configuration: { deviceDescription: { nodeId, views }, viewDescription: oldViewDescription } } = getState();
654 let dataPath = `/rests/operations/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`;
655 let viewSpecification: ViewSpecification = views[0];
656 let viewElement: ViewElement;
657 let dataMember: string;
658 let embedList: boolean = false;
659 let isNew: string | false = false;
661 let currentNS: string | null = null;
662 let defaultNS: string | null = null;
664 dispatch(new SetCollectingSelectionData(true));
666 for (let ind = 0; ind < pathParts.length; ++ind) {
667 let [property, key] = pathParts[ind];
668 const namespaceInd = property && property.indexOf(":") || -1;
669 const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
671 if (ind === 0) { defaultNS = namespace };
672 viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
673 if (!viewElement) throw Error("Property [" + property + "] does not exist.");
675 if (isViewElementList(viewElement) && !key) {
677 // if (viewElement && viewElement.isList && viewSpecification.parentView === "0") {
678 // throw new Error("Found a list at root level of a module w/o a refenrece key.");
680 // if (pathParts.length - 1 > ind) {
681 // dispatch(new SetCollectingSelectionData(false));
682 // throw new Error("No key for list [" + property + "]");
683 // } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
684 // // handle new element
685 // key = viewElement.key && String(data[viewElement.key]) || "";
688 // dispatch(new SetCollectingSelectionData(false));
689 // throw new Error("No value for key [" + viewElement.key + "] in list [" + property + "]");
694 dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`;
695 dataMember = viewElement.label;
698 if (viewElement && "viewId" in viewElement) {
699 viewSpecification = views[+viewElement.viewId];
700 } else if (viewElement.uiType === "rpc") {
701 viewSpecification = views[+(viewElement.inputViewId || 0)];
705 // re-inflate formerly flatten rpc data
706 data = data && Object.keys(data).reduce < { [name: string ]: any }>((acc, cur) => {
707 const pathParts = cur.split(".");
709 const updatePath = (obj: any, key: string) => {
710 obj[key] = (pos >= pathParts.length)
712 : updatePath(obj[key] || {}, pathParts[pos++]);
715 updatePath(acc, pathParts[pos++]);
719 // embed the list -> key: list
721 ? { [viewElement!.label]: data }
724 // embed the first element list[key]
729 // do not post root member (0)
730 if ((viewSpecification && viewSpecification.id !== "0") || (dataMember! && !data)) {
731 const updateResult = await restService.executeRpc(dataPath, { [`${defaultNS}:input`]: data || {} });
732 if (updateResult.status < 200 || updateResult.status > 299) {
733 const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || "";
734 throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`);
736 dispatch(new UpdatOutputData(updateResult.data));
738 throw new Error(`There is NO RPC specified.`);
742 // // update display specification
745 dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` }));
748 dispatch(new SetCollectingSelectionData(false));