un-cli 0.0.81 → 0.0.83

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 CHANGED
@@ -14,12 +14,14 @@ npm install -g un-cli
14
14
  ## Once installed here is how you connect
15
15
 
16
16
  ```bash
17
- > uncli --portal https://utilitynetwork.esri.com/portal --service NapervilleElectric_SQLServer --user tester --password tester.108
17
+ > uncli --portal https://utilitynetwork.esri.com/portal --service NapervilleElectric_SQLServer --user tester --password tester.108 --verify true
18
18
  ```
19
+ If this fails with a verification error it means you are using a self-signed certificate you can use use --verify false to disable verification but don't use this in production.
19
20
 
20
21
  ```bash
21
22
 
22
23
  uncli> help
24
+
23
25
  ┌───────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
24
26
  │ (index) │ Values │
25
27
  ├───────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
@@ -40,10 +42,15 @@ uncli> help
40
42
  │ update subnetworks --all │ 'Update all dirty subnetworks synchronously' │
41
43
  │ update subnetworks --deleted │ 'Update all deleted dirty subnetworks synchronously' │
42
44
  │ update subnetworks --all --async │ 'Update all dirty subnetworks asynchronously' │
43
- export subnetworks --all 'Export all subnetworks with ACK '
44
- export subnetworks --new "Export all subnetworks with ACK that haven't been exported "
45
+ export subnetworks --all [--folder] 'Export all subnetworks with ACK --folder where exported files are saved'
46
+ export subnetworks --new [--folder] "Export all subnetworks with ACK that haven't been exported --folder where exported files are saved"
45
47
  │ export subnetworks --deleted │ 'Export all subnetworks with ACK that are deleted ' │
46
- count 'Lists the number of rows in all feature layers.'
48
+ updateisconnected 'Run update is connected '
49
+ │ versions │ 'List all versions available to the current logged in user.' │
50
+ │ reconcile --version <version name> │ 'Reconcile the input version synchronously' │
51
+ │ reconcile --all │ 'Reconcile all versions available to the current user synchronously' │
52
+ │ reconcile --all --async │ 'Reconcile all versions available to the current user asynchronously' │
53
+ │ count │ 'Lists the number of rows in all feature layers and tables.' │
47
54
  │ count --system │ 'Lists the number of rows in system layers.' │
48
55
  │ connect --service │ 'Connects to the another service' │
49
56
  │ tracelogs --age <minutes> │ 'Lists utility network trace summary logs for the last x minutes (requires admin)' │
package/adminlog.mjs CHANGED
@@ -20,6 +20,8 @@ export class AdminLog {
20
20
  return fetch;
21
21
  }
22
22
  }
23
+
24
+
23
25
  async query (codes, serviceName ="*", pageSize = 100000, startTime = null, endTime = null, logLevel = "DEBUG")
24
26
  {
25
27
  const url = this.adminServerUrl + "/logs/query?f=pjson"
package/index.html CHANGED
@@ -2144,6 +2144,11 @@ function numberWithCommas(x) {
2144
2144
  })
2145
2145
  }
2146
2146
 
2147
+ function decodeHTMLEntities (x) {
2148
+ const decodedString = document.createElement('div');
2149
+ decodedString.innerHTML = x;
2150
+ return decodedString.innerHTML;
2151
+ }
2147
2152
 
2148
2153
  //paging works from newer message walking to the oldest message
2149
2154
  //start time must be less than end time
@@ -2190,7 +2195,11 @@ function numberWithCommas(x) {
2190
2195
  let jsonRes = await result.json()
2191
2196
  let allMessages = [].concat(jsonRes.logMessages)
2192
2197
  allMessages = allMessages.filter(m => m.message.indexOf(messageFilter) > -1 && m.methodName.indexOf(methodFilter) > -1)
2193
-
2198
+ //add decode html
2199
+ allMessages = allMessages.map(m=> {
2200
+ m.message = decodeHTMLEntities(m.message)
2201
+ return m
2202
+ })
2194
2203
  let oldStart = undefined;
2195
2204
  let newStartTime = undefined;
2196
2205
  while (jsonRes.hasMore && !stop)
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.81";
10
+ let version = "0.0.83";
11
11
  const GENERATE_TOKEN_TIME_MIN = 30;
12
12
 
13
13
  let rl = null;
@@ -178,9 +178,14 @@ const inputs = {
178
178
  "export subnetworks --deleted": "Export all subnetworks with ACK that are deleted ",
179
179
  "updateisconnected": "Run update is connected ",
180
180
  "versions": "List all versions available to the current logged in user.",
181
+ "versions --summary": "Summary of versions.",
182
+ "versions --unreconciled": "List all versions that haven't been reconciled.",
183
+ "versions --version <version name>": "List the input version info",
181
184
  "reconcile --version <version name>": "Reconcile the input version synchronously",
182
- "reconcile --all": "Reconcile all versions available to the current user synchronously",
183
- "reconcile --all --async": "Reconcile all versions available to the current user asynchronously",
185
+ "reconcile --withpost --version <version name>": "Reconcile & Post the input version synchronously, oldest common ancestor first",
186
+ "reconcile --all": "Reconcile all versions available to the current user synchronously, oldest common ancestor first",
187
+ "reconcile --all --withpost --async": "Reconcile and post all versions available to the current user asynchronously, oldest common ancestor first",
188
+ "reconcile --all --async": "Reconcile all versions available to the current user asynchronously, oldest common ancestor first",
184
189
  "count": "Lists the number of rows in all feature layers and tables.",
185
190
  "count --system": "Lists the number of rows in system layers.",
186
191
  "connect --service": "Connects to the another service",
@@ -233,23 +238,100 @@ const inputs = {
233
238
 
234
239
  console.table(serviceDef)
235
240
  },
241
+
242
+
236
243
 
237
- "^versions$": async () => {
244
+ "^versions --version": async (input) => {
245
+
238
246
 
247
+ const inputParam = input.match(/--version .*/gm)
248
+ let versionName = null;
249
+ if (inputParam != null && inputParam.length > 0)
250
+ versionName = inputParam[0].replace("--version ", "")
251
+
252
+
253
+ let versions = await un.versions();
254
+ versions.versions = versions.versions.filter ( v => v.versionName.toString().toUpperCase() == versionName.toUpperCase())
255
+
256
+
257
+ if (versions.versions.length === 0) {
258
+ logger.info("No versions found.")
259
+ return;
260
+ }
261
+ const subs = versions.versions.sort ( (a,b)=> (a?.commonAncestorDate - b?.commonAncestorDate) ). map( (a) => {
262
+ return {"versionName": a.versionName, "Id": a.versionId, "guid" : a.versionGuid, "modified": new Date(a.modifiedDate), "common": a.commonAncestorDate ? new Date( a.commonAncestorDate) : 'N/A' , "reconciled": a.reconcileDate ? new Date(a.reconcileDate) : 'N/A'};
263
+ })
264
+
265
+ console.table(subs)
266
+ const rowCount = subs.length;
267
+ logger.info (`${numberWithCommas(rowCount)} rows returned.`)
268
+ },
269
+
270
+
271
+
272
+ "^versions --unreconciled$": async () => {
239
273
  const versions = await un.versions();
240
274
  if (versions.versions.length === 0) {
241
275
  logger.info("No versions found.")
242
276
  return;
243
277
  }
244
- const subs = versions.versions.map( (a) => {
245
- return {"versionName": a.versionName, "Id": a.versionId, "guid" : a.versionGuid, "created": a.creationDate, "modified": a.modifiedDate, "access": a.access};
246
- })
278
+
279
+ const subs = versions.versions.filter( a => a.reconcileDate != null ).sort ( (a,b)=> (a?.commonAncestorDate - b?.commonAncestorDate) ). map( (a) => {
280
+ return {"versionName": a.versionName, "Id": a.versionId, "guid" : a.versionGuid, "modified": new Date(a.modifiedDate), "common": a.commonAncestorDate ? new Date( a.commonAncestorDate) : 'N/A' , "reconciled": a.reconcileDate ? new Date(a.reconcileDate) : 'N/A'};
281
+ })
247
282
 
248
283
  console.table(subs)
249
284
  const rowCount = subs.length;
250
285
  logger.info (`${numberWithCommas(rowCount)} rows returned.`)
251
286
  },
252
287
 
288
+ "^versions$": async () => {
289
+ const versions = await un.versions();
290
+ if (versions.versions.length === 0) {
291
+ logger.info("No versions found.")
292
+ return;
293
+ }
294
+ const subs = versions.versions.sort ( (a,b)=> (a?.commonAncestorDate - b?.commonAncestorDate) ). map( (a) => {
295
+ return {"versionName": a.versionName, "Id": a.versionId, "guid" : a.versionGuid, "modified": new Date(a.modifiedDate), "common": a.commonAncestorDate ? new Date( a.commonAncestorDate) : 'N/A' , "reconciled": a.reconcileDate ? new Date(a.reconcileDate) : 'N/A'};
296
+ })
297
+
298
+ console.table(subs)
299
+ const rowCount = subs.length;
300
+ logger.info (`${numberWithCommas(rowCount)} rows returned.`)
301
+ },
302
+
303
+
304
+ "^versions --summary$": async () => {
305
+ /*
306
+ total versions,
307
+ total unreconciled versions
308
+ total versions behind default
309
+ */
310
+ const versions = await un.versions();
311
+ if (versions.versions.length === 0) {
312
+ logger.info("No versions found.")
313
+ return;
314
+ }
315
+
316
+ const summary = {
317
+ "totalVersions": 0,
318
+ "unreconciledVersions": 0,
319
+ "versionsBehindDefault": 0,
320
+ "defaultMoment": 0
321
+ }
322
+
323
+ const defaultVersion = versions.versions.filter(v => v.versionName.toString().toUpperCase() === "SDE.DEFAULT")[0];
324
+
325
+ summary.totalVersions = versions.versions.length;
326
+
327
+ summary.unreconciledVersions = versions.versions.filter( a => a.reconcileDate != null ).length
328
+
329
+ summary.versionsBehindDefault = versions.versions.filter( a => defaultVersion.modifiedDate > a?.commonAncestorDate ).length
330
+
331
+ summary.defaultMoment = (new Date(defaultVersion.modifiedDate)).toString()
332
+ console.table(summary)
333
+ },
334
+
253
335
 
254
336
  "^reconcile --version": async (input) => {
255
337
 
@@ -258,8 +340,14 @@ const inputs = {
258
340
  if (inputParam != null && inputParam.length > 0)
259
341
  versionName = inputParam[0].replace("--version ", "")
260
342
 
261
- const versions = await un.versions();
343
+ let versions = await un.versions();
344
+ versions.versions = versions.versions.filter ( v => v.versionName.toString().toUpperCase() == versionName.toUpperCase())
262
345
 
346
+ if (versions.versions.length ==0 )
347
+ {
348
+ logger.info (`Version not found ${versionName}`)
349
+ return;
350
+ }
263
351
  for (let v = 0; v < versions.versions.length; v++)
264
352
  {
265
353
  if (versions.versions[v].versionName.toString().toUpperCase() === "SDE.DEFAULT") continue;
@@ -271,19 +359,55 @@ const inputs = {
271
359
  break;
272
360
  }
273
361
 
362
+ }
363
+
364
+ logger.info (`Reconciled ${versionName}`)
365
+ },
366
+
367
+
368
+
369
+ "^reconcile --withpost --version": async (input) => {
370
+
371
+ const inputParam = input.match(/--version .*/gm)
372
+ let versionName = null;
373
+ if (inputParam != null && inputParam.length > 0)
374
+ versionName = inputParam[0].replace("--version ", "")
375
+
376
+ let versions = await un.versions()
377
+ versions.versions = versions.versions.filter ( v => v.versionName.toString().toUpperCase() == versionName.toUpperCase())
378
+
379
+ if (versions.versions.length ==0 )
380
+ {
381
+ logger.info (`Version not found ${versionName}`)
382
+ return;
383
+ }
384
+
385
+ for (let v = 0; v < versions.versions.length; v++)
386
+ {
387
+ if (versions.versions[v].versionName.toString().toUpperCase() === "SDE.DEFAULT") continue;
388
+ if (versions.versions[v].versionName.toString().toUpperCase() == versionName.toUpperCase()) {
389
+ logger.info (`Reconciling and Posting version ${versions.versions[v].versionName} Common Ancestor ${new Date(versions.versions[v].commonAncestorDate)} ...`)
390
+
391
+ const result = await un.reconcile(versions.versions[v].versionGuid, true, false, true, false);
392
+ logger.info(JSON.stringify(result))
393
+ break;
394
+ }
395
+
274
396
  }
275
- logger.info (`Reconciled ${versionName}.`)
397
+ logger.info (`Reconciled and Posted ${versionName}.`)
276
398
  },
277
399
 
400
+
278
401
 
279
402
  "^reconcile --all$": async () => {
280
403
 
281
- const versions = await un.versions();
404
+ let versions = await un.versions()
405
+ versions.versions = versions.versions.sort ( (a,b)=> (a?.commonAncestorDate - b?.commonAncestorDate) )
282
406
 
283
407
  for (let v = 0; v < versions.versions.length; v++)
284
408
  {
285
409
  if (versions.versions[v].versionName.toString().toUpperCase() === "SDE.DEFAULT") continue;
286
- logger.info (`Reconciling version ${versions.versions[v].versionName} ...`)
410
+ logger.info (`Reconciling version ${versions.versions[v].versionName} Common Ancestor ${new Date(versions.versions[v].commonAncestorDate)} ...`)
287
411
 
288
412
  const result = await un.reconcile(versions.versions[v].versionGuid, false, false, true, false);
289
413
  logger.info(JSON.stringify(result))
@@ -294,12 +418,13 @@ const inputs = {
294
418
 
295
419
  "^reconcile --all --async$": async () => {
296
420
  //async
297
- const versions = await un.versions();
421
+ let versions = await un.versions()
422
+ versions.versions = versions.versions.sort ( (a,b)=> (a?.commonAncestorDate - b?.commonAncestorDate) )
298
423
 
299
424
  for (let v = 0; v < versions.versions.length; v++)
300
425
  {
301
426
  if (versions.versions[v].versionName.toString().toUpperCase() === "SDE.DEFAULT") continue;
302
- logger.info (`Reconciling version ${versions.versions[v].versionName} ...`)
427
+ logger.info (`Reconciling version ${versions.versions[v].versionName} Common Ancestor ${new Date(versions.versions[v].commonAncestorDate)} ...`)
303
428
 
304
429
  const result = await un.reconcile(versions.versions[v].versionGuid, false, false, true, true);
305
430
  logger.info(JSON.stringify(result))
@@ -308,7 +433,23 @@ const inputs = {
308
433
  logger.info (`Reconciled ${numberWithCommas(rowCount)} versions.`)
309
434
  },
310
435
 
311
-
436
+ "^reconcile --all --withpost --async$": async () => {
437
+ //async
438
+ let versions = await un.versions()
439
+ versions.versions = versions.versions.sort ( (a,b)=> (a?.commonAncestorDate - b?.commonAncestorDate) )
440
+
441
+ for (let v = 0; v < versions.versions.length; v++)
442
+ {
443
+ if (versions.versions[v].versionName.toString().toUpperCase() === "SDE.DEFAULT") continue;
444
+ logger.info (`Reconciling & Posting version ${versions.versions[v].versionName} Common Ancestor ${new Date(versions.versions[v].commonAncestorDate)} ...`)
445
+
446
+ const result = await un.reconcile(versions.versions[v].versionGuid, true, false, true, true);
447
+ logger.info(JSON.stringify(result))
448
+ }
449
+ const rowCount = versions.versions.length;
450
+ logger.info (`Reconciled & Posted ${numberWithCommas(rowCount)} versions.`)
451
+ },
452
+
312
453
  "^versions --disconnect$": async () => {
313
454
  //disconnect all versions
314
455
  const versions = await un.versions();
@@ -931,7 +1072,7 @@ const inputs = {
931
1072
 
932
1073
  const arMessages = allMessages
933
1074
  .filter(m => m.message.indexOf("Attribute rule execution complete:") > -1)
934
- .map (m => JSON.parse(m.message.replace("Attribute rule execution complete:", "")))
1075
+ .map (m => JSON.parse(decodeHTMLEntities(m.message.replace("Attribute rule execution complete:", ""))))
935
1076
  .map( m => {
936
1077
  m["Elapsed Time (ms)"] = Math.round(m["Elapsed Time"]*1000000)/1000
937
1078
  // m["Arcade Evaluation Time:"] = Math.round(m["Arcade Evaluation Time:"]*1000,6)
@@ -1007,7 +1148,19 @@ const inputs = {
1007
1148
  },
1008
1149
 
1009
1150
 
1010
-
1151
+ "^reconcilelogs --age" : async input => {
1152
+ const topLogCount = 1000;
1153
+ const pageSize = 10000
1154
+
1155
+ const inputParam = input.match(/--age .*/gm)
1156
+ let mins = 30; //query logs for the last 30 minutes
1157
+ if (inputParam != null && inputParam.length > 0)
1158
+ mins = inputParam[0].replace("--age ", "")
1159
+
1160
+
1161
+ reconcileLogs(mins, parameters.service)
1162
+
1163
+ },
1011
1164
  "^validatelogs --age": async input => {
1012
1165
 
1013
1166
  const topLogCount = 1000;
@@ -1279,7 +1432,7 @@ const inputs = {
1279
1432
 
1280
1433
  const arMessages = allMessages
1281
1434
  .filter(m => m.message.indexOf("Attribute rule execution complete:") > -1)
1282
- .map (m => JSON.parse(m.message.replace("Attribute rule execution complete:", "")))
1435
+ .map (m => JSON.parse(decodeHTMLEntities(m.message.replace("Attribute rule execution complete:", ""))))
1283
1436
  .map( m => {
1284
1437
  m["Elapsed Time (ms)"] = Math.round(m["Elapsed Time"]*1000000)/1000
1285
1438
  // m["Arcade Evaluation Time:"] = Math.round(m["Arcade Evaluation Time:"]*1000,6)
@@ -1539,6 +1692,101 @@ export async function run (){
1539
1692
  }
1540
1693
 
1541
1694
 
1695
+ function decodeHTMLEntities (x) {
1696
+ const entities = {
1697
+ '&lt;': '<',
1698
+ '&gt;': '>',
1699
+ '&amp;': '&',
1700
+ '&quot;': '"',
1701
+ '&apos;': "'"
1702
+ };
1703
+
1704
+ const y = x.replace(/&[a-zA-Z0-9#]+;/g, (match) => entities[match] || match);
1705
+ return y
1706
+ }
1707
+
1708
+
1709
+
1710
+ async function reconcileLogs (mins, service) {
1711
+
1712
+
1713
+ parameters.service = service
1714
+
1715
+
1716
+ console.log(`Querying reconcile logs for ${parameters.service} for the last ${mins} minutes ...`)
1717
+
1718
+ //startTime is the most recent
1719
+ //endTime is the oldest
1720
+
1721
+
1722
+ //page query the admin log , search for /applyEdits logs by methodname
1723
+ let allMessages = await adminLog.query(mins, parameters.service, [102003,102024,102023], "", "DEBUG")
1724
+
1725
+
1726
+
1727
+ //build out the dictionary, key is request id, value is another dictionary
1728
+ const queryLogs = {}
1729
+ //sort by time
1730
+ allMessages = allMessages.sort ( (m1, m2) => m2.time - m1.time )
1731
+ allMessages.forEach (m => {
1732
+
1733
+ if (!queryLogs[m.requestID])
1734
+ queryLogs[m.requestID] = {"message": "Time,Method,Elapsed_ms,Message"}
1735
+
1736
+ queryLogs[m.requestID].message += "\r\n" + m.time + "," + m.methodName + "," + Math.round(m.elapsed*1000) + "," + m.message
1737
+
1738
+ //get elapsed
1739
+ //check for async (method GPReconcileVersionAsync::Execute)
1740
+ //sync
1741
+ //VersionManagementServer::HandleREST_ReconcileOperation
1742
+ //message Returned moment:
1743
+ if (m.message.indexOf("Returned moment: ")> -1 &&
1744
+ ( m.methodName.indexOf("VersionManagementServer::HandleREST_ReconcileOperation") > -1 ||
1745
+ m.methodName.indexOf("GPReconcileVersionAsync::Execute") > -1
1746
+ )
1747
+ )
1748
+ {
1749
+ queryLogs[m.requestID].elapsed = m.elapsed
1750
+ queryLogs[m.requestID].source = m.source.replace(".MapServer", "")
1751
+ queryLogs[m.requestID].user = m.user
1752
+ queryLogs[m.requestID].time = m.time
1753
+ queryLogs[m.requestID].requestID = m.requestID
1754
+ queryLogs[m.requestID].methodName = m.methodName
1755
+
1756
+ }
1757
+
1758
+ if (m.message.indexOf("EndReconcile;") > -1)
1759
+ {
1760
+ queryLogs[m.requestID].gdbVersion = m.message.replace("EndReconcile;","")
1761
+
1762
+
1763
+ }
1764
+
1765
+
1766
+
1767
+ })
1768
+
1769
+ allMessages = []
1770
+
1771
+ Object.keys(queryLogs).forEach(k =>
1772
+ {
1773
+ const m = queryLogs[k]
1774
+ if (m.methodName)
1775
+ allMessages.push(m)
1776
+
1777
+ })
1778
+
1779
+
1780
+
1781
+ console.log ("Filtering messages...")
1782
+
1783
+ allMessages = filterMessages(allMessages)
1784
+ .sort( (m1,m2) => Math.round(m2.elapsed*1000) -Math.round(m1.elapsed*1000))
1785
+ console.table(allMessages)
1786
+
1787
+ }
1788
+
1789
+
1542
1790
 
1543
1791
  function printFishnet(fishnet) {
1544
1792
 
package/makerequest.mjs CHANGED
@@ -31,13 +31,10 @@ export function makeRequest (opts) {
31
31
 
32
32
  let nodeFetch = await import ("node-fetch");
33
33
  f = nodeFetch.default;
34
- }
35
- catch(ex) {
36
- f = fetch;
37
- }
34
+
38
35
  //set a proxy if one exists
39
36
 
40
- if (process.env['HTTPS_PROXY'])
37
+ if (process?.env['HTTPS_PROXY'])
41
38
  {
42
39
  try {
43
40
  const HttpsProxyAgent = await import ('https-proxy-agent')
@@ -48,7 +45,11 @@ export function makeRequest (opts) {
48
45
  }
49
46
 
50
47
  }
51
-
48
+ }
49
+ catch(ex) {
50
+ f = fetch;
51
+ }
52
+
52
53
  const options = {
53
54
  "method" : opts.method,
54
55
  "headers": headers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "un-cli",
3
- "version": "0.0.81",
3
+ "version": "0.0.83",
4
4
  "description": "Command line interface for working with ArcGIS Utility Network Extension",
5
5
  "main": "index.mjs",
6
6
  "bin": {
package/un.mjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node --experimental-modules
2
-
3
- import {run} from "./index.mjs"
1
+ #!/usr/bin/env node --experimental-modules
2
+
3
+ import {run} from "./index.mjs"
4
4
  run();
package/cmd.txt DELETED
@@ -1,3 +0,0 @@
1
- reconcile --version U136967.MigrateTransm
2
- update subnetworks --all
3
- exit