un-cli 0.0.85 → 0.0.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -34
- package/index.html +335 -26
- package/index.mjs +59 -2
- package/package.json +1 -1
- package/utilitynetwork.node.mjs +1 -0
package/README.md
CHANGED
|
@@ -1,17 +1,68 @@
|
|
|
1
1
|
# uncli
|
|
2
|
-
utility network javascript command line works on Windows and Linux
|
|
2
|
+
Server Log Parser and utility network javascript command line works on Windows and Linux
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Server Log web parser
|
|
7
|
+
This tool also provides a way to parse server logs and view them for each utility network and geodatabase functions. (Note you do not need to install nodejs for the web parser)
|
|
8
|
+
|
|
9
|
+
### IIS
|
|
10
|
+
To install on IIS follow these steps: This server assume your machine name where the Webadaptor lives is `utilitynetwork.esri.com`
|
|
11
|
+
|
|
12
|
+
- Login to machine where you have the webadaptor
|
|
13
|
+
- Create a folder called `log` in `c:\inetpub\wwwroot`
|
|
14
|
+
- Copy the entire content of the repro https://github.com/hussein-nasser/uncli to c:\inetpub\wwwroot\log
|
|
15
|
+
- Open IIS (Internet Information Services)
|
|
16
|
+
- Open MIME Types
|
|
17
|
+
- Under action click Add
|
|
18
|
+
- under file extension type `.mjs` and under MIME Type write `application/javascript`
|
|
19
|
+
- Restart IIS
|
|
20
|
+
- (OPTIONAL only change when your webadaptor is not /portal) In c:\inetpub\wwwroot\log edit the `index.html` and find this line of code
|
|
21
|
+
- Update the JSON object with the correct URL, for example if your webadaptor is https://utilitynetwork.esri.com/portal set the "portal" to be that. You don't have to update the referer it will be automatically calculated it.
|
|
22
|
+
- (Optional) if you have multiple federated server , you have to set the correct one you want to query admin api for in the `"server"` parameter below.
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
let parameters = {
|
|
26
|
+
"user": "unadmin",
|
|
27
|
+
"password": "",
|
|
28
|
+
"portal": "",
|
|
29
|
+
"service": "",
|
|
30
|
+
"referer": "",
|
|
31
|
+
"server": undefined
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
- Save the HTML and then visit `https://utilitynetwork.esri.com/log` put in your username and password and login to use the parser
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Tomcat or any other similar Webservers
|
|
38
|
+
-Create a folder under tomcat/webapps/ call it log
|
|
39
|
+
-Then copy the content there.
|
|
40
|
+
-Then go to /home/tomcat/conf/web.xml
|
|
41
|
+
-And add this entry if doesn’t exist
|
|
42
|
+
```xml
|
|
43
|
+
<mime-mapping>
|
|
44
|
+
<extension>mjs</extension>
|
|
45
|
+
<mime-type>text/javascript</mime-type>
|
|
46
|
+
</mime-mapping>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
-Restart tomcat and visit the page/log should work
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Utility Network Command Line tool (Utility Network Only)
|
|
55
|
+
### Download Node JS 13.5 or later
|
|
5
56
|
|
|
6
57
|
https://nodejs.org/en/
|
|
7
58
|
|
|
8
|
-
|
|
59
|
+
### Open command prompt and run this command
|
|
9
60
|
|
|
10
61
|
```bash
|
|
11
62
|
npm install -g un-cli
|
|
12
63
|
```
|
|
13
64
|
|
|
14
|
-
|
|
65
|
+
### Once installed here is how you connect
|
|
15
66
|
|
|
16
67
|
```bash
|
|
17
68
|
> uncli --portal https://utilitynetwork.esri.com/portal --service NapervilleElectric_SQLServer --user tester --password tester.108 --verify true
|
|
@@ -67,7 +118,7 @@ uncli> help
|
|
|
67
118
|
|
|
68
119
|
|
|
69
120
|
|
|
70
|
-
|
|
121
|
+
### Execute bulk of commands
|
|
71
122
|
Create a commands.txt file and type in the commands in that file
|
|
72
123
|
command.txt
|
|
73
124
|
```text
|
|
@@ -77,32 +128,3 @@ export subnetworks --new
|
|
|
77
128
|
|
|
78
129
|
|
|
79
130
|
> uncli --portal https://utilitynetwork.esri.com/portal --service NapervilleElectric_SQLServer --user tester --password tester.108 --file commands.txt --verify true
|
|
80
|
-
|
|
81
|
-
## Server Log web parser
|
|
82
|
-
This tool also provides a way to parse server logs and view them for each utility network and geodatabase function
|
|
83
|
-
|
|
84
|
-
To install on IIS follow these steps: This server assume your machine name where the Webadaptor lives is `utilitynetwork.esri.com`
|
|
85
|
-
|
|
86
|
-
- Login to machine where you have the webadaptor
|
|
87
|
-
- Create a folder called `log` in `c:\inetpub\wwwroot`
|
|
88
|
-
- Copy the entire content of the repro https://github.com/hussein-nasser/uncli to c:\inetpub\wwwroot\log
|
|
89
|
-
- Open IIS (Internet Information Services)
|
|
90
|
-
- Open MIME Types
|
|
91
|
-
- Under action click Add
|
|
92
|
-
- under file extension type `.mjs` and under MIME Type write `application/javascript`
|
|
93
|
-
- Restart IIS
|
|
94
|
-
- (OPTIONAL only change when your webadaptor is not /portal) In c:\inetpub\wwwroot\log edit the `index.html` and find this line of code
|
|
95
|
-
- Update the JSON object with the correct URL, for example if your webadaptor is https://utilitynetwork.esri.com/portal set the "portal" to be that. You don't have to update the referer it will be automatically calculated it.
|
|
96
|
-
- (Optional) if you have multiple federated server , you have to set the correct one you want to query admin api for in the `"server"` parameter below.
|
|
97
|
-
|
|
98
|
-
```js
|
|
99
|
-
let parameters = {
|
|
100
|
-
"user": "unadmin",
|
|
101
|
-
"password": "",
|
|
102
|
-
"portal": "",
|
|
103
|
-
"service": "",
|
|
104
|
-
"referer": "",
|
|
105
|
-
"server": undefined
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
- Save the HTML and then visit `https://utilitynetwork.esri.com/log` put in your username and password and login to use the parser
|
package/index.html
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<td>Method Name</td>
|
|
55
55
|
<td><input id = 'txtMethodName' type = 'text'></td>
|
|
56
56
|
<td>RequestID</td>
|
|
57
|
-
<td colspan =3><input id = 'txtRequestId' type = 'text'></td>
|
|
57
|
+
<td colspan =3><input id = 'txtRequestId' type = 'text' style="width:250px"></td>
|
|
58
58
|
</tr>
|
|
59
59
|
</table>
|
|
60
60
|
|
|
@@ -69,13 +69,15 @@
|
|
|
69
69
|
<button id = 'btnApplyEditsLogs'>ApplyEdits Logs</button>
|
|
70
70
|
<button id = 'btnQueryLogs'>Query Logs</button>
|
|
71
71
|
<button id = 'btnReconcileLogs'>Reconcile Logs</button>
|
|
72
|
+
<button id = 'btnstartEditingLogs'>StartEditing Logs</button>
|
|
72
73
|
<button id = 'btnstopEditingLogs'>StopEditing Logs</button>
|
|
73
|
-
<button id = 'btnWaitLogs'>Wait Logs</button>
|
|
74
|
+
<button id = 'btnWaitLogs'>Startup and Wait Logs</button>
|
|
74
75
|
<button id = 'btnDownloadLogs'>Raw Logs</button>
|
|
75
76
|
</td>
|
|
76
77
|
|
|
77
78
|
</tr>
|
|
78
79
|
<tr><td colspan=9><small>Elapsed time is in minutes:seconds:milliseconds</small></td></tr>
|
|
80
|
+
<tr><td colspan=9><small>To capture the logs effectively, enable debug logging from server manager, login as admin, run the operation (e.g. edit in Pro) and click the corresponding logs to load.</small></td></tr>
|
|
79
81
|
</table>
|
|
80
82
|
|
|
81
83
|
<table id = 'tblResult' border =1 cellpadding = 4 cellspacing = 4 style="text-align:left">
|
|
@@ -170,6 +172,7 @@
|
|
|
170
172
|
document.getElementById("btnDownloadLogs").disabled = false
|
|
171
173
|
document.getElementById("btnReconcileLogs").disabled = false
|
|
172
174
|
document.getElementById("btnstopEditingLogs").disabled = false
|
|
175
|
+
document.getElementById("btnstartEditingLogs").disabled = false
|
|
173
176
|
document.getElementById("btnWaitLogs").disabled = false
|
|
174
177
|
|
|
175
178
|
|
|
@@ -239,7 +242,9 @@
|
|
|
239
242
|
|
|
240
243
|
x = /Moment:.*\r\n/.exec(m.message);
|
|
241
244
|
if (x && x.length > 0) newMessage.moment = x[0].replace("Moment:", "").replace("\r\n", "") * 1000000
|
|
242
|
-
|
|
245
|
+
|
|
246
|
+
x = /Moment:\s*([\d.]+)/.exec(m.message);
|
|
247
|
+
if (x) newMessage.moment = parseFloat(x[1]) * 1000000;
|
|
243
248
|
|
|
244
249
|
}
|
|
245
250
|
catch(ex){
|
|
@@ -261,8 +266,6 @@
|
|
|
261
266
|
cTime.textContent = "Time"
|
|
262
267
|
const cUser = document.createElement("th");
|
|
263
268
|
cUser.textContent = "User"
|
|
264
|
-
const cMethod = document.createElement("th");
|
|
265
|
-
cMethod.textContent = "Method"
|
|
266
269
|
const cVersion = document.createElement("th");
|
|
267
270
|
cVersion.textContent = "Version"
|
|
268
271
|
const cMoment = document.createElement("th");
|
|
@@ -273,6 +276,8 @@
|
|
|
273
276
|
cTraceType.textContent = "Trace Type"
|
|
274
277
|
const cTraceTime = document.createElement("th");
|
|
275
278
|
cTraceTime.textContent = "Trace Time"
|
|
279
|
+
const cReadAmp = document.createElement("th");
|
|
280
|
+
cReadAmp.textContent = "Elements / Pages"
|
|
276
281
|
const cRequestId = document.createElement("th");
|
|
277
282
|
cRequestId.textContent = "RequestId"
|
|
278
283
|
const cLog = document.createElement("th");
|
|
@@ -280,11 +285,11 @@
|
|
|
280
285
|
header.appendChild (cService)
|
|
281
286
|
header.appendChild (cTime)
|
|
282
287
|
header.appendChild (cUser)
|
|
283
|
-
header.appendChild (cMethod)
|
|
284
288
|
header.appendChild (cVersion)
|
|
285
289
|
header.appendChild (cMoment)
|
|
286
290
|
header.appendChild (cTraceType)
|
|
287
291
|
header.appendChild (cTraceTime)
|
|
292
|
+
header.appendChild (cReadAmp)
|
|
288
293
|
header.appendChild (cRequestId)
|
|
289
294
|
header.appendChild (cElapsedMS)
|
|
290
295
|
|
|
@@ -306,6 +311,8 @@
|
|
|
306
311
|
cTraceType.textContent = m.traceType
|
|
307
312
|
const cTraceTime = document.createElement("td");
|
|
308
313
|
cTraceTime.textContent = msToMinSecMs(m.traceTime)
|
|
314
|
+
const cReadAmp = document.createElement("td");
|
|
315
|
+
cReadAmp.textContent = ParseTraceReadAmplication(m.message)
|
|
309
316
|
const cRequestId = document.createElement("td");
|
|
310
317
|
cRequestId.textContent = m.requestID
|
|
311
318
|
const cVersion = document.createElement("td");
|
|
@@ -319,11 +326,11 @@
|
|
|
319
326
|
logRow.appendChild (cService)
|
|
320
327
|
logRow.appendChild (cTime)
|
|
321
328
|
logRow.appendChild (cUser)
|
|
322
|
-
logRow.appendChild (cMethod)
|
|
323
329
|
logRow.appendChild (cVersion)
|
|
324
330
|
logRow.appendChild (cMoment)
|
|
325
331
|
logRow.appendChild (cTraceType)
|
|
326
332
|
logRow.appendChild (cTraceTime)
|
|
333
|
+
logRow.appendChild (cReadAmp)
|
|
327
334
|
logRow.appendChild (cRequestId)
|
|
328
335
|
logRow.appendChild (cElapsedMS)
|
|
329
336
|
|
|
@@ -379,9 +386,16 @@
|
|
|
379
386
|
let x ;
|
|
380
387
|
x = /Extent:.*\r\n/.exec(m.message);
|
|
381
388
|
if (x && x.length > 0) m.extent = x[0].replace("Extent:", "").replace("\r\n", "")
|
|
389
|
+
|
|
390
|
+
x = /"extent":\{[^}]+\}/.exec(m.message);
|
|
391
|
+
if (x) m.extent = x[0].replace(`"extent":`,"")
|
|
392
|
+
|
|
382
393
|
x = /Version name:.*\r\n/.exec(m.message);
|
|
383
394
|
if (x && x.length > 0) m.versionName = x[0].replace("Version name:", "").replace("\r\n", "")
|
|
384
395
|
|
|
396
|
+
x = /Version name:(.*)/.exec(m.message);
|
|
397
|
+
if (x) m.versionName = x[1].trim();
|
|
398
|
+
|
|
385
399
|
let re = new RegExp(`Network built. [-+]?([0-9]*\\.[0-9]+|[0-9]+) seconds \\([-+]?([0-9]*\\.[0-9]+|[0-9]+) total\\)`)
|
|
386
400
|
let res = re.exec(m.message)
|
|
387
401
|
|
|
@@ -510,6 +524,7 @@
|
|
|
510
524
|
|
|
511
525
|
const cRequestId = document.createElement("th");
|
|
512
526
|
cRequestId.textContent = "RequestId"
|
|
527
|
+
|
|
513
528
|
const cLog = document.createElement("th");
|
|
514
529
|
cLog.textContent = "Full Log"
|
|
515
530
|
|
|
@@ -547,6 +562,12 @@
|
|
|
547
562
|
|
|
548
563
|
const cRequestId = document.createElement("td");
|
|
549
564
|
cRequestId.textContent = m.requestID;
|
|
565
|
+
cRequestId.style.color = "blue";
|
|
566
|
+
cRequestId.style.textDecoration = "underline";
|
|
567
|
+
cRequestId.style.cursor = "pointer";
|
|
568
|
+
cRequestId.style.fontOpticalSizing
|
|
569
|
+
cRequestId.addEventListener("click", e=> viewRawLogs( m.requestID ) );
|
|
570
|
+
|
|
550
571
|
const cLog = document.createElement("td");
|
|
551
572
|
cLog.textContent = "..."
|
|
552
573
|
cLog.fullLog = m.message;
|
|
@@ -678,6 +699,12 @@ async function loadUpdateSubnetworkLogs () {
|
|
|
678
699
|
cSubnetworkName.textContent = m.subnetworkName
|
|
679
700
|
const cRequestId = document.createElement("td");
|
|
680
701
|
cRequestId.textContent = m.requestID
|
|
702
|
+
cRequestId.style.color = "blue";
|
|
703
|
+
cRequestId.style.textDecoration = "underline";
|
|
704
|
+
cRequestId.style.cursor = "pointer";
|
|
705
|
+
cRequestId.style.fontOpticalSizing
|
|
706
|
+
cRequestId.addEventListener("click", e=> viewRawLogs( m.requestID ) );
|
|
707
|
+
|
|
681
708
|
const cLog = document.createElement("td");
|
|
682
709
|
cLog.textContent = "..."
|
|
683
710
|
cLog.fullLog = m.message;
|
|
@@ -719,7 +746,7 @@ async function loadAttributeRules () {
|
|
|
719
746
|
|
|
720
747
|
const arMessages = filterMessages(allMessages)
|
|
721
748
|
.filter(m => m.message.indexOf("Attribute rule executed: ") > -1)
|
|
722
|
-
.map (m =>
|
|
749
|
+
.map (m => decodeAndParseJSON(m.message.replace("Attribute rule executed: ", "")))
|
|
723
750
|
.map( m => {
|
|
724
751
|
m["Elapsed Time (ms)"] = Math.round(m["Elapsed Time"]*1000000)/1000
|
|
725
752
|
// m["Arcade Evaluation Time:"] = Math.round(m["Arcade Evaluation Time:"]*1000,6)
|
|
@@ -1016,7 +1043,8 @@ console.log(`Querying applyEdits logs for ${parameters.service} for the last ${m
|
|
|
1016
1043
|
if (m.message.indexOf("GraphicFeatureServer::HandleRESTRequest ## inputJSON ") > -1)
|
|
1017
1044
|
{
|
|
1018
1045
|
applyEditslogs[m.requestID].message = m.message.replace("GraphicFeatureServer::HandleRESTRequest ## inputJSON = ","")
|
|
1019
|
-
|
|
1046
|
+
applyEditslogs[m.requestID].message = decodeHTMLEntities(applyEditslogs[m.requestID].message)
|
|
1047
|
+
const applyEditsPayload = decodeAndParseJSON(applyEditslogs[m.requestID].message)
|
|
1020
1048
|
applyEditslogs[m.requestID].gdbVersion = applyEditsPayload.gdbVersion
|
|
1021
1049
|
applyEditslogs[m.requestID].ids = [...new Set(applyEditsPayload.edits.reduce ( (prev, cur) => {prev.push(cur.id); return prev}, []) )];
|
|
1022
1050
|
}
|
|
@@ -1066,6 +1094,13 @@ console.log(`Querying applyEdits logs for ${parameters.service} for the last ${m
|
|
|
1066
1094
|
const cRequestId = document.createElement("td");
|
|
1067
1095
|
cRequestId.textContent = m.requestID
|
|
1068
1096
|
|
|
1097
|
+
cRequestId.style.color = "blue";
|
|
1098
|
+
cRequestId.style.textDecoration = "underline";
|
|
1099
|
+
cRequestId.style.cursor = "pointer";
|
|
1100
|
+
cRequestId.style.fontOpticalSizing
|
|
1101
|
+
cRequestId.addEventListener("click", e=> viewRawLogs( m.requestID ) );
|
|
1102
|
+
|
|
1103
|
+
|
|
1069
1104
|
const cLog = document.createElement("td");
|
|
1070
1105
|
const cLogText = document.createElement("input")
|
|
1071
1106
|
cLogText.type = "text"
|
|
@@ -1097,6 +1132,7 @@ console.log(`Querying applyEdits logs for ${parameters.service} for the last ${m
|
|
|
1097
1132
|
|
|
1098
1133
|
async function stopEditingLogs () {
|
|
1099
1134
|
|
|
1135
|
+
|
|
1100
1136
|
//build table
|
|
1101
1137
|
const tblResult = document.getElementById("tblResult");
|
|
1102
1138
|
//clear
|
|
@@ -1109,7 +1145,7 @@ parameters.service = cmbService.options[cmbService.selectedIndex].text
|
|
|
1109
1145
|
let mins = document.getElementById("txtAge").value //query logs for the last 30 minutes
|
|
1110
1146
|
|
|
1111
1147
|
|
|
1112
|
-
console.log(`Querying
|
|
1148
|
+
console.log(`Querying start editing logs for ${parameters.service} for the last ${mins} minutes ...`)
|
|
1113
1149
|
|
|
1114
1150
|
//startTime is the most recent
|
|
1115
1151
|
//endTime is the oldest
|
|
@@ -1158,20 +1194,22 @@ console.log(`Querying stop editing logs for ${parameters.service} for the last $
|
|
|
1158
1194
|
const queryLogs = {}
|
|
1159
1195
|
//sort by time
|
|
1160
1196
|
allMessages = allMessages.sort ( (m1, m2) => m2.time - m1.time )
|
|
1197
|
+
// allMessages = allMessages.filter( m => m.methodName.indexOf("Geodatabase.FeatureDataset.StartEditing") > -1 )
|
|
1161
1198
|
allMessages.forEach (m => {
|
|
1162
1199
|
|
|
1163
1200
|
if (!queryLogs[m.requestID])
|
|
1164
1201
|
queryLogs[m.requestID] = {"message": "Time,Method,Elapsed_ms,Message"}
|
|
1165
1202
|
|
|
1166
|
-
|
|
1203
|
+
if (m.methodName.indexOf("Geodatabase.FeatureDataset.StopEditing") > -1)
|
|
1204
|
+
queryLogs[m.requestID].message += "\r\n" + m.time + "," + m.methodName + "," + Math.round(m.elapsed*1000) + "," + m.message
|
|
1167
1205
|
|
|
1168
1206
|
//get elapsed
|
|
1169
1207
|
//check for async (method GPReconcileVersionAsync::Execute)
|
|
1170
1208
|
//sync
|
|
1171
1209
|
//VersionManagementServer::HandleREST_ReconcileOperation
|
|
1172
1210
|
//message Returned moment:
|
|
1173
|
-
if ( m.methodName.indexOf("
|
|
1174
|
-
&& m.message.indexOf("
|
|
1211
|
+
if ( m.methodName.indexOf("Geodatabase.FeatureDataset.StopEditing") > -1
|
|
1212
|
+
&& m.message.indexOf("Starting editing") > -1
|
|
1175
1213
|
)
|
|
1176
1214
|
{
|
|
1177
1215
|
queryLogs[m.requestID].elapsed = m.elapsed
|
|
@@ -1180,13 +1218,13 @@ console.log(`Querying stop editing logs for ${parameters.service} for the last $
|
|
|
1180
1218
|
queryLogs[m.requestID].time = m.time
|
|
1181
1219
|
queryLogs[m.requestID].requestID = m.requestID
|
|
1182
1220
|
queryLogs[m.requestID].methodName = m.methodName.replace("VersionManagementServer::HandleREST_","")
|
|
1183
|
-
|
|
1221
|
+
queryLogs[m.requestID].textMessage = m.message
|
|
1184
1222
|
}
|
|
1185
1223
|
|
|
1186
1224
|
if (m.message.indexOf("In WorkspaceInfo::GetVersionedWorkspaceInfo : Cache hit for versionOrBranchName=") > -1)
|
|
1187
1225
|
{
|
|
1188
|
-
let v = m.message.replace("
|
|
1189
|
-
v = v.substr(v.indexOf("
|
|
1226
|
+
let v = m.message.replace(" In WorkspaceInfo::GetVersionedWorkspaceInfo : Cache hit for versionOrBranchName=","")
|
|
1227
|
+
v = v.substr(v.indexOf(":")+1)
|
|
1190
1228
|
queryLogs[m.requestID].gdbVersion =v
|
|
1191
1229
|
|
|
1192
1230
|
}
|
|
@@ -1195,6 +1233,7 @@ console.log(`Querying stop editing logs for ${parameters.service} for the last $
|
|
|
1195
1233
|
|
|
1196
1234
|
})
|
|
1197
1235
|
|
|
1236
|
+
/*
|
|
1198
1237
|
allMessages = []
|
|
1199
1238
|
|
|
1200
1239
|
Object.keys(queryLogs).forEach(k =>
|
|
@@ -1205,14 +1244,17 @@ console.log(`Querying stop editing logs for ${parameters.service} for the last $
|
|
|
1205
1244
|
|
|
1206
1245
|
})
|
|
1207
1246
|
|
|
1208
|
-
|
|
1247
|
+
*/
|
|
1209
1248
|
|
|
1210
1249
|
console.log ("Filtering messages...")
|
|
1211
|
-
|
|
1250
|
+
allMessages = allMessages.filter( m => m.methodName.indexOf("Geodatabase.FeatureDataset.StopEditing") > -1 )
|
|
1251
|
+
|
|
1212
1252
|
allMessages = filterMessages(allMessages)
|
|
1213
1253
|
.sort( (m1,m2) => Math.round(m2.elapsed*1000) -Math.round(m1.elapsed*1000))
|
|
1214
1254
|
|
|
1215
1255
|
allMessages.forEach (m => {
|
|
1256
|
+
|
|
1257
|
+
|
|
1216
1258
|
const logRow = document.createElement("tr");
|
|
1217
1259
|
|
|
1218
1260
|
const cService = document.createElement("th");
|
|
@@ -1225,21 +1267,27 @@ console.log(`Querying stop editing logs for ${parameters.service} for the last $
|
|
|
1225
1267
|
cUser.textContent = m.user
|
|
1226
1268
|
|
|
1227
1269
|
const cVersion = document.createElement("td");
|
|
1228
|
-
cVersion.textContent =
|
|
1270
|
+
cVersion.textContent = queryLogs[m.requestID].gdbVersion
|
|
1229
1271
|
|
|
1230
1272
|
const cMethodName = document.createElement("td");
|
|
1231
1273
|
cMethodName.textContent = m.methodName
|
|
1232
1274
|
|
|
1233
1275
|
const cRequestId = document.createElement("td");
|
|
1234
|
-
cRequestId.textContent = m.requestID
|
|
1235
|
-
|
|
1276
|
+
cRequestId.textContent = m.requestID
|
|
1277
|
+
|
|
1278
|
+
cRequestId.style.color = "blue";
|
|
1279
|
+
cRequestId.style.textDecoration = "underline";
|
|
1280
|
+
cRequestId.style.cursor = "pointer";
|
|
1281
|
+
cRequestId.style.fontOpticalSizing
|
|
1282
|
+
cRequestId.addEventListener("click", e=> viewRawLogs( m.requestID ) );
|
|
1283
|
+
|
|
1236
1284
|
const cTotalTime = document.createElement("td");
|
|
1237
1285
|
cTotalTime.textContent = msToMinSecMs(m.elapsed*1000 )
|
|
1238
1286
|
const cLog = document.createElement("td");
|
|
1239
|
-
cLog.textContent =
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1287
|
+
cLog.textContent = m.message
|
|
1288
|
+
// cLog.title = "Click to copy full startediting log"
|
|
1289
|
+
// cLog.fullLog = m.message;
|
|
1290
|
+
// cLog.addEventListener("click", e=> {navigator.clipboard.writeText(e.target.fullLog); alert("Copied to clipboard")})
|
|
1243
1291
|
|
|
1244
1292
|
//cLog.fullLog = m.payLoad;
|
|
1245
1293
|
//cLog.title = m.payLoad + "\nClick to copy"
|
|
@@ -1267,6 +1315,197 @@ console.log(`Querying stop editing logs for ${parameters.service} for the last $
|
|
|
1267
1315
|
|
|
1268
1316
|
|
|
1269
1317
|
|
|
1318
|
+
async function viewRawLogs(requestId){
|
|
1319
|
+
|
|
1320
|
+
const txtRequestId = document.getElementById("txtRequestId")
|
|
1321
|
+
txtRequestId.value = requestId;
|
|
1322
|
+
document.getElementById("btnDownloadLogs").click();
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
async function startEditingLogs () {
|
|
1329
|
+
|
|
1330
|
+
//build table
|
|
1331
|
+
const tblResult = document.getElementById("tblResult");
|
|
1332
|
+
//clear
|
|
1333
|
+
while(tblResult.firstChild) tblResult.removeChild(tblResult.firstChild)
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
parameters.service = cmbService.options[cmbService.selectedIndex].text
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
let mins = document.getElementById("txtAge").value //query logs for the last 30 minutes
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
console.log(`Querying start editing logs for ${parameters.service} for the last ${mins} minutes ...`)
|
|
1343
|
+
|
|
1344
|
+
//startTime is the most recent
|
|
1345
|
+
//endTime is the oldest
|
|
1346
|
+
|
|
1347
|
+
|
|
1348
|
+
//page query the admin log , search for /stopediting logs by methodname
|
|
1349
|
+
let allMessages = await adminLogQueryWithPaging(mins, parameters.service, [102003,17003], "", "DEBUG")
|
|
1350
|
+
|
|
1351
|
+
|
|
1352
|
+
while(tblResult.firstChild) tblResult.removeChild(tblResult.firstChild)
|
|
1353
|
+
|
|
1354
|
+
let i =0;
|
|
1355
|
+
|
|
1356
|
+
const header = document.createElement("tr");
|
|
1357
|
+
const cService = document.createElement("th");
|
|
1358
|
+
cService.textContent = "Source"
|
|
1359
|
+
const cTime = document.createElement("th");
|
|
1360
|
+
cTime.textContent = "Time"
|
|
1361
|
+
const cUser = document.createElement("th");
|
|
1362
|
+
cUser.textContent = "User"
|
|
1363
|
+
const cVersion = document.createElement("th");
|
|
1364
|
+
cVersion.textContent = "Version"
|
|
1365
|
+
const cMethodName = document.createElement("th");
|
|
1366
|
+
cMethodName.textContent = "MethodName"
|
|
1367
|
+
const cRequestId = document.createElement("th");
|
|
1368
|
+
cRequestId.textContent = "RequestId"
|
|
1369
|
+
const cTotalTime = document.createElement("th");
|
|
1370
|
+
cTotalTime.title = "Total time startEditing took"
|
|
1371
|
+
cTotalTime.textContent = "Total Time"
|
|
1372
|
+
|
|
1373
|
+
|
|
1374
|
+
const cLog = document.createElement("th");
|
|
1375
|
+
cLog.textContent = "StartEditing details"
|
|
1376
|
+
header.appendChild (cService)
|
|
1377
|
+
header.appendChild (cTime)
|
|
1378
|
+
header.appendChild (cUser)
|
|
1379
|
+
header.appendChild (cVersion)
|
|
1380
|
+
header.appendChild (cMethodName)
|
|
1381
|
+
header.appendChild (cRequestId)
|
|
1382
|
+
header.appendChild (cTotalTime)
|
|
1383
|
+
|
|
1384
|
+
header.appendChild (cLog)
|
|
1385
|
+
tblResult.appendChild(header)
|
|
1386
|
+
|
|
1387
|
+
//build out the dictionary, key is request id, value is another dictionary
|
|
1388
|
+
const queryLogs = {}
|
|
1389
|
+
//sort by time
|
|
1390
|
+
allMessages = allMessages.sort ( (m1, m2) => m2.time - m1.time )
|
|
1391
|
+
// allMessages = allMessages.filter( m => m.methodName.indexOf("Geodatabase.FeatureDataset.StartEditing") > -1 )
|
|
1392
|
+
allMessages.forEach (m => {
|
|
1393
|
+
|
|
1394
|
+
if (!queryLogs[m.requestID])
|
|
1395
|
+
queryLogs[m.requestID] = {"message": "Time,Method,Elapsed_ms,Message"}
|
|
1396
|
+
|
|
1397
|
+
if (m.methodName.indexOf("Geodatabase.FeatureDataset.StartEditing") > -1)
|
|
1398
|
+
queryLogs[m.requestID].message += "\r\n" + m.time + "," + m.methodName + "," + Math.round(m.elapsed*1000) + "," + m.message
|
|
1399
|
+
|
|
1400
|
+
//get elapsed
|
|
1401
|
+
//check for async (method GPReconcileVersionAsync::Execute)
|
|
1402
|
+
//sync
|
|
1403
|
+
//VersionManagementServer::HandleREST_ReconcileOperation
|
|
1404
|
+
//message Returned moment:
|
|
1405
|
+
if ( m.methodName.indexOf("Geodatabase.FeatureDataset.StartEditing") > -1
|
|
1406
|
+
&& m.message.indexOf("Starting editing") > -1
|
|
1407
|
+
)
|
|
1408
|
+
{
|
|
1409
|
+
queryLogs[m.requestID].elapsed = m.elapsed
|
|
1410
|
+
queryLogs[m.requestID].source = m.source.replace(".MapServer", "")
|
|
1411
|
+
queryLogs[m.requestID].user = m.user
|
|
1412
|
+
queryLogs[m.requestID].time = m.time
|
|
1413
|
+
queryLogs[m.requestID].requestID = m.requestID
|
|
1414
|
+
queryLogs[m.requestID].methodName = m.methodName.replace("VersionManagementServer::HandleREST_","")
|
|
1415
|
+
queryLogs[m.requestID].textMessage = m.message
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
if (m.message.indexOf("In WorkspaceInfo::GetVersionedWorkspaceInfo : Cache hit for versionOrBranchName=") > -1)
|
|
1419
|
+
{
|
|
1420
|
+
let v = m.message.replace(" In WorkspaceInfo::GetVersionedWorkspaceInfo : Cache hit for versionOrBranchName=","")
|
|
1421
|
+
v = v.substr(v.indexOf(":")+1)
|
|
1422
|
+
queryLogs[m.requestID].gdbVersion =v
|
|
1423
|
+
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
|
|
1428
|
+
})
|
|
1429
|
+
|
|
1430
|
+
/*
|
|
1431
|
+
allMessages = []
|
|
1432
|
+
|
|
1433
|
+
Object.keys(queryLogs).forEach(k =>
|
|
1434
|
+
{
|
|
1435
|
+
const m = queryLogs[k]
|
|
1436
|
+
if (m.methodName)
|
|
1437
|
+
allMessages.push(m)
|
|
1438
|
+
|
|
1439
|
+
})
|
|
1440
|
+
|
|
1441
|
+
*/
|
|
1442
|
+
|
|
1443
|
+
console.log ("Filtering messages...")
|
|
1444
|
+
allMessages = allMessages.filter( m => m.methodName.indexOf("Geodatabase.FeatureDataset.StartEditing") > -1 )
|
|
1445
|
+
|
|
1446
|
+
allMessages = filterMessages(allMessages)
|
|
1447
|
+
.sort( (m1,m2) => Math.round(m2.elapsed*1000) -Math.round(m1.elapsed*1000))
|
|
1448
|
+
|
|
1449
|
+
allMessages.forEach (m => {
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
const logRow = document.createElement("tr");
|
|
1453
|
+
|
|
1454
|
+
const cService = document.createElement("th");
|
|
1455
|
+
cService.textContent = m.source
|
|
1456
|
+
|
|
1457
|
+
const cTime = document.createElement("td");
|
|
1458
|
+
cTime.textContent = new Date(m.time).toLocaleString()
|
|
1459
|
+
|
|
1460
|
+
const cUser = document.createElement("td");
|
|
1461
|
+
cUser.textContent = m.user
|
|
1462
|
+
|
|
1463
|
+
const cVersion = document.createElement("td");
|
|
1464
|
+
cVersion.textContent = queryLogs[m.requestID].gdbVersion
|
|
1465
|
+
|
|
1466
|
+
const cMethodName = document.createElement("td");
|
|
1467
|
+
cMethodName.textContent = m.methodName
|
|
1468
|
+
|
|
1469
|
+
const cRequestId = document.createElement("td");
|
|
1470
|
+
cRequestId.textContent = m.requestID
|
|
1471
|
+
cRequestId.style.color = "blue";
|
|
1472
|
+
cRequestId.style.textDecoration = "underline";
|
|
1473
|
+
cRequestId.style.cursor = "pointer";
|
|
1474
|
+
cRequestId.style.fontOpticalSizing
|
|
1475
|
+
cRequestId.addEventListener("click", e=> viewRawLogs( m.requestID ) );
|
|
1476
|
+
|
|
1477
|
+
const cTotalTime = document.createElement("td");
|
|
1478
|
+
cTotalTime.textContent = msToMinSecMs(m.elapsed*1000 )
|
|
1479
|
+
const cLog = document.createElement("td");
|
|
1480
|
+
cLog.textContent = m.message
|
|
1481
|
+
// cLog.title = "Click to copy full startediting log"
|
|
1482
|
+
// cLog.fullLog = m.message;
|
|
1483
|
+
// cLog.addEventListener("click", e=> {navigator.clipboard.writeText(e.target.fullLog); alert("Copied to clipboard")})
|
|
1484
|
+
|
|
1485
|
+
//cLog.fullLog = m.payLoad;
|
|
1486
|
+
//cLog.title = m.payLoad + "\nClick to copy"
|
|
1487
|
+
// cLog.addEventListener("click", e=> {navigator.clipboard.writeText(e.target.fullLog); e.target.selectionStart = 0; e.target.selectionEnd = e.target.value.length;
|
|
1488
|
+
// alert("Copied to clipboard") })
|
|
1489
|
+
logRow.appendChild (cService)
|
|
1490
|
+
logRow.appendChild (cTime)
|
|
1491
|
+
logRow.appendChild (cUser)
|
|
1492
|
+
logRow.appendChild (cVersion)
|
|
1493
|
+
logRow.appendChild (cMethodName)
|
|
1494
|
+
logRow.appendChild (cRequestId)
|
|
1495
|
+
logRow.appendChild (cTotalTime)
|
|
1496
|
+
|
|
1497
|
+
logRow.appendChild (cLog)
|
|
1498
|
+
tblResult.appendChild(logRow)
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
addCSV(allMessages)
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
|
|
1270
1509
|
|
|
1271
1510
|
|
|
1272
1511
|
async function waitLogs () {
|
|
@@ -1642,7 +1881,8 @@ console.log(`Querying query logs for ${parameters.service} for the last ${mins}
|
|
|
1642
1881
|
if (m.message.indexOf("GraphicFeatureServer::HandleRESTRequest ## inputJSON = ") > -1)
|
|
1643
1882
|
{
|
|
1644
1883
|
queryLogs[m.requestID].message = m.message.replace("GraphicFeatureServer::HandleRESTRequest ## inputJSON = ","")
|
|
1645
|
-
|
|
1884
|
+
queryLogs[m.requestID].message = decodeHTMLEntities(queryLogs[m.requestID].message)
|
|
1885
|
+
const queryPayload = decodeAndParseJSON(queryLogs[m.requestID].message)
|
|
1646
1886
|
queryLogs[m.requestID].gdbVersion = queryPayload.gdbVersion
|
|
1647
1887
|
queryLogs[m.requestID].moment = queryPayload.historicMoment
|
|
1648
1888
|
queryLogs[m.requestID].methodName = m.methodName;
|
|
@@ -1942,6 +2182,9 @@ function numberWithCommas(x) {
|
|
|
1942
2182
|
btnstopEditingLogs.disabled = true
|
|
1943
2183
|
|
|
1944
2184
|
|
|
2185
|
+
const btnstartEditingLogs = document.getElementById("btnstartEditingLogs");
|
|
2186
|
+
btnstartEditingLogs.addEventListener("click", e => startEditingLogs())
|
|
2187
|
+
btnstartEditingLogs.disabled = true
|
|
1945
2188
|
|
|
1946
2189
|
const btnWait = document.getElementById("btnWaitLogs");
|
|
1947
2190
|
btnWait.addEventListener("click", e => waitLogs())
|
|
@@ -2144,6 +2387,12 @@ function numberWithCommas(x) {
|
|
|
2144
2387
|
})
|
|
2145
2388
|
}
|
|
2146
2389
|
|
|
2390
|
+
|
|
2391
|
+
function decodeAndParseJSON (x) {
|
|
2392
|
+
const y = decodeHTMLEntities(x);
|
|
2393
|
+
return JSON.parse(y);
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2147
2396
|
function decodeHTMLEntities (x) {
|
|
2148
2397
|
const decodedString = document.createElement('div');
|
|
2149
2398
|
decodedString.innerHTML = x;
|
|
@@ -2218,6 +2467,14 @@ function numberWithCommas(x) {
|
|
|
2218
2467
|
|
|
2219
2468
|
allMessages = allMessages.concat(jsonRes.logMessages.filter(m => m.message.indexOf(messageFilter) > -1 && m.methodName.indexOf(methodFilter) > -1))
|
|
2220
2469
|
}
|
|
2470
|
+
//DECODE html for all messages.
|
|
2471
|
+
logger.info(`Decoding HTML entities ${allMessages.length}`);
|
|
2472
|
+
|
|
2473
|
+
allMessages = allMessages.map(m=> {
|
|
2474
|
+
m.message = decodeHTMLEntities(m.message)
|
|
2475
|
+
return m
|
|
2476
|
+
})
|
|
2477
|
+
|
|
2221
2478
|
logger.info(`Completed aggregation total messages ${allMessages.length}`);
|
|
2222
2479
|
|
|
2223
2480
|
return allMessages
|
|
@@ -2225,6 +2482,58 @@ function numberWithCommas(x) {
|
|
|
2225
2482
|
}
|
|
2226
2483
|
|
|
2227
2484
|
|
|
2485
|
+
|
|
2486
|
+
function ParseTraceReadAmplication (traceLog) {
|
|
2487
|
+
|
|
2488
|
+
try {
|
|
2489
|
+
|
|
2490
|
+
|
|
2491
|
+
const text = traceLog;
|
|
2492
|
+
const traversedMatch = text.match(/Total\s+#\s*Traversed\s+Elements:\s*([\d,]+)/i);
|
|
2493
|
+
|
|
2494
|
+
const traversedElements = traversedMatch
|
|
2495
|
+
? parseInt(traversedMatch[1].replace(/,/g, ''), 10)
|
|
2496
|
+
: 0;
|
|
2497
|
+
|
|
2498
|
+
let minPagesRead = traversedElements / 1024;
|
|
2499
|
+
if (minPagesRead <= 1024) minPagesRead =1;
|
|
2500
|
+
|
|
2501
|
+
|
|
2502
|
+
// -------------------------------------------------------------------
|
|
2503
|
+
// Sum all Cache misses values
|
|
2504
|
+
// -------------------------------------------------------------------
|
|
2505
|
+
let actualPagesRead = 0;
|
|
2506
|
+
|
|
2507
|
+
// Match rows that begin with a table name and then a cache miss number
|
|
2508
|
+
const cacheMissRegex =
|
|
2509
|
+
/^\s*[A-Za-z0-9:#()\-\s]+?\s+(\d+)\s+\d+\s+\d+\s+\d+\s+\d+/gm;
|
|
2510
|
+
|
|
2511
|
+
let match;
|
|
2512
|
+
|
|
2513
|
+
while ((match = cacheMissRegex.exec(text)) !== null) {
|
|
2514
|
+
actualPagesRead += parseInt(match[1], 10);
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
|
|
2518
|
+
let readAmp;
|
|
2519
|
+
if (actualPagesRead < minPagesRead)
|
|
2520
|
+
readAmp= "0%"
|
|
2521
|
+
else
|
|
2522
|
+
readAmp= Math.floor((actualPagesRead-minPagesRead)/actualPagesRead *100) + "%"
|
|
2523
|
+
|
|
2524
|
+
readAmp= numberWithCommas(traversedElements) + " / " + numberWithCommas(actualPagesRead)
|
|
2525
|
+
|
|
2526
|
+
console.log(`traversedElements = ${traversedElements}, minPagesRead = ${minPagesRead}, actualPagesRead = ${actualPagesRead} ,, readAmp=${readAmp}` );
|
|
2527
|
+
|
|
2528
|
+
return readAmp
|
|
2529
|
+
}
|
|
2530
|
+
catch(ex){
|
|
2531
|
+
console.error(ex)
|
|
2532
|
+
return "n/a";
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
|
|
2228
2537
|
|
|
2229
2538
|
|
|
2230
2539
|
</script>
|
package/index.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { AdminLog } from "./adminlog.mjs"
|
|
|
7
7
|
import logger from "./logger.mjs"
|
|
8
8
|
import fetch from "node-fetch"
|
|
9
9
|
//update version
|
|
10
|
-
let version = "0.0.
|
|
10
|
+
let version = "0.0.87";
|
|
11
11
|
const GENERATE_TOKEN_TIME_MIN = 30;
|
|
12
12
|
|
|
13
13
|
let rl = null;
|
|
@@ -166,6 +166,7 @@ const inputs = {
|
|
|
166
166
|
"subnetworks --deleted" : "Lists dirty and deleted subnetworks",
|
|
167
167
|
"evaluate" : "Evaluate in parallel",
|
|
168
168
|
"trace --subnetwork <subnetwork>": "Traces input subnetwork and returns the time and number of elements returned .",
|
|
169
|
+
"trace --all": "Traces all subnetworks.",
|
|
169
170
|
"topology" : "Displays the topology status",
|
|
170
171
|
"topology --disable" : "Disable topology",
|
|
171
172
|
"topology --enable" : "Enable topology",
|
|
@@ -175,7 +176,6 @@ const inputs = {
|
|
|
175
176
|
"update subnetworks --all --async": "Update all dirty subnetworks asynchronously",
|
|
176
177
|
"update subnetworks --deleted": "Update all deleted dirty subnetworks synchronously",
|
|
177
178
|
"update subnetworks --all --async": "Update all dirty subnetworks asynchronously",
|
|
178
|
-
|
|
179
179
|
"export subnetworks --all [--folder]": "Export all subnetworks with ACK --folder where exported files are saved",
|
|
180
180
|
"export subnetworks --new [--folder]": "Export all subnetworks with ACK that haven't been exported --folder where exported files are saved",
|
|
181
181
|
"export subnetworks --deleted": "Export all subnetworks with ACK that are deleted ",
|
|
@@ -1043,6 +1043,63 @@ const inputs = {
|
|
|
1043
1043
|
logger.error(JSON.stringify(ex))
|
|
1044
1044
|
}
|
|
1045
1045
|
},
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
"^trace --all": async input => {
|
|
1050
|
+
//get subnetwork name
|
|
1051
|
+
try {
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
const inputDir= "Exported"
|
|
1055
|
+
let full = true;
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
const subnetworks = await un.getSubnetworks();
|
|
1059
|
+
if (subnetworks.features.length === 0) {
|
|
1060
|
+
logger.info("No dirty subnetworks found.")
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
const subs = subnetworks.features.map(a => a.attributes)
|
|
1065
|
+
const rowCount = subs.length;
|
|
1066
|
+
logger.info (`${numberWithCommas(rowCount)} subnetworks returned.`)
|
|
1067
|
+
for (let i = 0; i < subs.length ; i++)
|
|
1068
|
+
{
|
|
1069
|
+
|
|
1070
|
+
const fromDate = new Date();
|
|
1071
|
+
const subnetworkName = v(subs[i],"subnetworkname")
|
|
1072
|
+
try{
|
|
1073
|
+
|
|
1074
|
+
logger.info(`Tracing subnetwork ${subnetworkName}`);
|
|
1075
|
+
const result = await un.subnetworkTraceSimple(subnetworkName)
|
|
1076
|
+
if (result == null) {
|
|
1077
|
+
logger.info(`Subnetwork ${subnetworkName} doesn't exist`);
|
|
1078
|
+
return null;
|
|
1079
|
+
}
|
|
1080
|
+
const toDate = new Date();
|
|
1081
|
+
const timeRun = toDate.getTime() - fromDate.getTime();
|
|
1082
|
+
const newResult = {}
|
|
1083
|
+
newResult.duration = numberWithCommas(Math.round(timeRun)) + " ms"
|
|
1084
|
+
newResult.elementsCount = result.traceResults.elements.length;
|
|
1085
|
+
console.table(newResult)
|
|
1086
|
+
}
|
|
1087
|
+
catch (ex)
|
|
1088
|
+
{
|
|
1089
|
+
logger.info(`Failed to trace ${subnetworkName} ${ex}`);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
}
|
|
1097
|
+
catch(ex){
|
|
1098
|
+
logger.error(JSON.stringify(ex))
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
|
|
1102
|
+
|
|
1046
1103
|
"^export subnetworks --deleted --folder" : async input => {
|
|
1047
1104
|
|
|
1048
1105
|
//create folder
|
package/package.json
CHANGED