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";
6 import { restService } from "../services/restServices";
7 import { YangParser } from "../yang/yangParser";
8 import { Module } from "../models/yang";
9 import { ViewSpecification, ViewElement, isViewElementReference, isViewElementList, isViewElementObjectOrList } from "../models/uiModels";
10 import { AddErrorInfoAction } from "../../../../framework/src/actions/errorActions";
12 export class EnableValueSelector extends Action {
13 constructor(public listSpecification: ViewSpecification, public listData: any[], public keyProperty: string, public onValueSelected : (value: any) => void ) {
18 export class SetCollectingSelectionData extends Action {
19 constructor(public busy: boolean) {
24 export class SetSelectedValue extends Action {
25 constructor(public value: any) {
30 export class UpdateDeviceDescription extends Action {
31 constructor( public nodeId: string, public modules: { [name:string]: Module}, public views: ViewSpecification[]) {
36 export class UpdatViewDescription extends Action {
37 constructor(public vPath: string, public view: ViewSpecification, public viewData: any, public displayAsList: boolean = false, public key?: string ) {
42 export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => {
43 const { configuration: { connectedNetworkElements : { rows }} } = getState();
44 dispatch(new SetCollectingSelectionData(true));
45 const networkElement = rows.find(r => r.nodeId === nodeId) || await restService.getMountedNetworkElementByMountId(nodeId);
46 if (!networkElement) {
47 console.error(new Error(`NetworkElement : [${nodeId}] does not exist.`));
48 return dispatch(new UpdateDeviceDescription("", { }, [ ]));
51 if (!networkElement.nodeDetails || !networkElement.nodeDetails.availableCapabilities) {
52 throw new Error(`NetworkElement : [${nodeId}] has no capabilities.`);
54 const parser = new YangParser();
56 const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i;
57 for (let i = 0; i < networkElement.nodeDetails.availableCapabilities.length; ++i){
58 const capRaw = networkElement.nodeDetails.availableCapabilities[i];
59 const capMatch = capRaw && capParser.exec(capRaw);
61 capMatch && await parser.addCapability(capMatch[2], capMatch[1]);
69 console.log(parser.modules, parser.views)
71 return dispatch(new UpdateDeviceDescription(nodeId, parser.modules, parser.views));
74 export const splitVPath = (vPath: string, vPathParser : RegExp): [string, string?][] => {
75 const pathParts: [string, string?][] = [];
76 let partMatch: RegExpExecArray | null;
78 partMatch = vPathParser.exec(vPath);
80 pathParts.push([partMatch[1], partMatch[2] || undefined]);
86 const getReferencedDataList = async (refPath: string, dataPath: string, modules: { [name: string]: Module }, views: ViewSpecification[]) => {
87 const pathParts = splitVPath(refPath, /(?:(?:([^\/\:]+):)?([^\/]+))/g); // 1 = opt: namespace / 2 = property
88 let referencedModule = modules[pathParts[0][0]];
90 let dataMember: string;
91 let view: ViewSpecification;
92 let currentNS: string | null = null;
93 let dataUrls = [dataPath];
96 for (let i = 0; i < pathParts.length; ++i) {
97 const [pathPartNS, pathPart] = pathParts[i];
98 const namespace = pathPartNS != null ? (currentNS = pathPartNS) : currentNS;
100 const viewElement = i === 0
101 ? views[0].elements[`${referencedModule.name}:${pathPart}`]
102 : view!.elements[`${pathPart}`] || view!.elements[`${namespace}:${pathPart}`];
104 if (!viewElement) throw new Error(`Could not find ${pathPart} in ${refPath}`);
105 if (i < pathParts.length - 1) {
106 if (!isViewElementObjectOrList(viewElement)) {
107 throw Error(`Module: [${referencedModule.name}].[${viewElement.label}]. Viewelement is not list or object.`);
109 view = views[+viewElement.viewId];
110 const resultingDataUrls : string[] = [];
111 if (isViewElementList(viewElement)) {
112 for (let j = 0; j < dataUrls.length; ++j) {
113 const dataUrl = dataUrls[j];
114 const restResult = (await restService.getConfigData(dataUrl));
115 if (restResult.data == null || restResult.status < 200 || restResult.status > 299) {
116 const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
117 throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
120 let dataRaw = restResult.data[dataMember!];
121 dataRaw = dataRaw instanceof Array
125 data = dataRaw && dataRaw[viewElement.label] || [];
126 const keys: string[] = data.map((entry: { [key: string]: any } )=> entry[viewElement.key!]);
127 resultingDataUrls.push(...keys.map(key => `${dataUrl}/${viewElement.label.replace(/\//ig, "%2F")}/${key.replace(/\//ig, "%2F")}`));
129 dataMember = viewElement.label;
131 // just a member, not a list
132 const pathSegment = (i === 0
133 ? `/${referencedModule.name}:${viewElement.label.replace(/\//ig, "%2F")}`
134 : `/${viewElement.label.replace(/\//ig, "%2F")}`);
135 resultingDataUrls.push(...dataUrls.map(dataUrl => dataUrl + pathSegment));
136 dataMember = viewElement.label;
138 dataUrls = resultingDataUrls;
141 for (let j = 0; j < dataUrls.length; ++j) {
142 const dataUrl = dataUrls[j];
143 const restResult = (await restService.getConfigData(dataUrl));
144 if (restResult.data == null || restResult.status < 200 || restResult.status > 299) {
145 const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
146 throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`);
148 let dataRaw = restResult.data[dataMember!];
149 dataRaw = dataRaw instanceof Array
154 // BUG UUID ist nicht in den elements enthalten !!!!!!
155 const key = viewElement && viewElement.label || pathPart;
166 const resolveViewDescription = (defaultNS: string | null, vPath: string, view: ViewSpecification, viewData: any, displayAsList: boolean = false, key?: string): UpdatViewDescription =>{
168 // check if-feature | when | and resolve all references.
170 view.elements = Object.keys(view.elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => {
171 const elm = view.elements[cur];
172 const key = defaultNS && cur.replace(new RegExp(`^${defaultNS}:`, "i"),"") || cur;
173 if (isViewElementReference(elm)) {
174 acc[key] = { ...(elm.ref(vPath) || elm), id: key };
176 acc[key] = { ...elm, id: key };
180 return new UpdatViewDescription(vPath, view, viewData, displayAsList, key);
183 export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
184 const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
185 const { configuration: { deviceDescription: { nodeId, modules, views } }, framework: { navigationState } } = getState();
186 let dataPath = `/restconf/config/network-topology:network-topology/topology/topology-netconf/node/${nodeId}/yang-ext:mount`;
187 let viewSpecification: ViewSpecification = views[0];
188 let viewElement: ViewElement;
190 let dataMember: string;
191 let extractList: boolean = false;
193 let currentNS : string | null = null;
194 let defaultNS : string | null = null;
196 dispatch(new SetCollectingSelectionData(true));
198 for (let ind = 0; ind < pathParts.length; ++ind) {
199 const [property, key] = pathParts[ind];
200 const namespaceInd = property && property.indexOf(":") || -1;
201 const namespace : string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
203 if (ind === 0) { defaultNS = namespace };
205 viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`];
206 if (!viewElement) throw Error("Property [" + property + "] does not exist.");
208 if (viewElement.isList && !key) {
209 if (pathParts.length - 1 > ind) {
210 dispatch(new SetCollectingSelectionData(false));
211 throw new Error("No key for list [" + property + "]");
212 } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
213 // empty key is used for new element
214 if (viewElement && "viewId" in viewElement) viewSpecification = views[+viewElement.viewId];
215 const data = Object.keys(viewSpecification.elements).reduce<{ [name: string]: any }>((acc, cur) => {
216 const elm = viewSpecification.elements[cur];
218 acc[elm.id] = elm.default || ""
222 return dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, data, false, isViewElementList(viewElement!) && viewElement.key || undefined));
224 if (viewElement && isViewElementList(viewElement) && viewSpecification.parentView === "0") {
225 // check if there is a reference as key
226 const listSpecification = views[+viewElement.viewId];
227 const keyElement = viewElement.key && listSpecification.elements[viewElement.key];
228 if (keyElement && isViewElementReference(keyElement)) {
229 const refList = await getReferencedDataList(keyElement.referencePath, dataPath, modules, views);
231 throw new Error(`Could not find refList for [${keyElement.referencePath}].`);
234 throw new Error(`Key property not found for [${keyElement.referencePath}].`);
236 dispatch(new EnableValueSelector(refList.view, refList.data, refList.key, (refKey) => {
237 window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, "%2F")}]`)));
240 dispatch(new SetCollectingSelectionData(false));
241 throw new Error("Found a list at root level of a module w/o a refenrece key.");
247 dataPath += `/${property}${key ? `/${key.replace(/\//ig, "%2F")}` : ""}`;
248 dataMember = namespace === defaultNS
250 : `${namespace}:${viewElement.label}`;
254 if (viewElement && "viewId" in viewElement) viewSpecification = views[+viewElement.viewId];
258 if (viewSpecification && viewSpecification.id !== "0") {
259 const restResult = (await restService.getConfigData(dataPath));
260 if (!restResult.data) {
261 // special case: if this is a list without any response
262 if (extractList && restResult.status === 404) {
263 if (!isViewElementList(viewElement!)) {
264 throw new Error(`vPath: [${vPath}]. ViewElement has no key.`);
266 return dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, [], extractList, viewElement.key));
268 throw new Error(`Did not get response from Server. Status: [${restResult.status}]`);
269 } else if (restResult.status < 200 || restResult.status > 299) {
270 const message = restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || "";
271 throw new Error(`Server Error. Status: [${restResult.status}]\n${message}`);
273 data = restResult.data[dataMember!]; // extract dataMember
276 // extract the first element list[key]
277 data = data instanceof Array
281 // extract the list -> key: list
283 ? data[viewElement!.label] || [] // if the list is empty, it does not exist
287 return dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, data, extractList, isViewElementList(viewElement!) && viewElement.key || undefined));
288 // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]
289 // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]/lp
292 dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not process ${dataPath}` }));
293 dispatch(new SetCollectingSelectionData(false));
299 export const updateDataActionAsyncCreator = (vPath: string, data: any) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => {
300 const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key
301 const { configuration: { deviceDescription: { nodeId, views } } } = getState();
302 let dataPath = `/restconf/config/network-topology:network-topology/topology/topology-netconf/node/${nodeId}/yang-ext:mount`;
303 let viewSpecification: ViewSpecification = views[0];
304 let viewElement: ViewElement;
305 let dataMember: string;
306 let embedList: boolean = false;
307 let isNew: string | false = false;
309 let currentNS: string | null = null;
310 let defaultNS: string | null = null;
312 dispatch(new SetCollectingSelectionData(true));
314 for (let ind = 0; ind < pathParts.length; ++ind) {
315 let [property, key] = pathParts[ind];
316 const namespaceInd = property && property.indexOf(":") || -1;
317 const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS;
319 if (ind === 0) { defaultNS = namespace };
320 viewElement = viewSpecification.elements[property];
321 if (!viewElement) throw Error("Property [" + property + "] does not exist.");
323 if (isViewElementList(viewElement) && !key) {
325 if (viewElement && viewElement.isList && viewSpecification.parentView === "0") {
326 throw new Error("Found a list at root level of a module w/o a refenrece key.");
328 if (pathParts.length - 1 > ind) {
329 dispatch(new SetCollectingSelectionData(false));
330 throw new Error("No key for list [" + property + "]");
331 } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) {
332 // handle new element
333 key = viewElement.key && String(data[viewElement.key]) || "";
336 dispatch(new SetCollectingSelectionData(false));
337 throw new Error("No value for key [" + viewElement.key +"] in list [" + property + "]");
342 dataPath += `/${property}${key ? `/${key.replace(/\//ig, "%2F")}` : ""}`;
343 dataMember = viewElement.label;
346 if (viewElement && "viewId" in viewElement) {
347 viewSpecification = views[+viewElement.viewId];
351 // embed the list -> key: list
353 ? { [viewElement!.label]: data }
356 // embed the first element list[key]
361 // do not extract root member (0)
362 if (viewSpecification && viewSpecification.id !== "0") {
363 const updateResult = await restService.setConfigData(dataPath, { [dataMember!]: data }); // extractDataMember
364 if (updateResult.status < 200 || updateResult.status > 299) {
365 const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || "";
366 throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`);
371 ? dispatch(new ReplaceAction(`/configuration/${nodeId}/${vPath.replace(/\[\]$/i,`[${isNew}]`)}`)) // navigate to new element
372 : dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, data, embedList, isViewElementList(viewElement!) && viewElement.key || undefined));
375 dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` }));
376 dispatch(new SetCollectingSelectionData(false));