un-cli 0.0.59 → 0.0.62

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/adminlog.mjs CHANGED
@@ -13,10 +13,10 @@ export class AdminLog {
13
13
  this.token = token;
14
14
  }
15
15
 
16
- query (codes, serviceName ="*", pageSize = 100000, startTime = null, endTime = null)
16
+ query (codes, serviceName ="*", pageSize = 100000, startTime = null, endTime = null, logLevel = "DEBUG")
17
17
  {
18
18
  const url = this.adminServerUrl + "/logs/query?f=pjson"
19
- const level = "DEBUG"
19
+ const level = logLevel
20
20
  const filterType="json"
21
21
  const token = this.token
22
22
  const filter = {
package/cmd.txt CHANGED
@@ -1 +1 @@
1
- arlogs --byrule
1
+ tracelogs --m 300
package/index.mjs CHANGED
@@ -7,13 +7,10 @@ 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.59";
10
+ let version = "0.0.62";
11
11
  const GENERATE_TOKEN_TIME_MIN = 30;
12
12
 
13
13
  let rl = null;
14
-
15
-
16
- //uncli --portal https://utilitynetwork.esri.com/portal --service AllStar_oracle --user unadmin --password unadmin.108
17
14
  let portal = null;
18
15
  let un = null;
19
16
  let adminLog = null;
@@ -29,9 +26,11 @@ function parseInput(){
29
26
  "--command",
30
27
  "--gdbversion",
31
28
  "--file",
32
- "--verify"
29
+ "--verify",
30
+ "--server"
33
31
  ]
34
-
32
+
33
+ //null marked parmaters are required
35
34
  const params = {
36
35
  "portal": null,
37
36
  "service": null,
@@ -40,7 +39,8 @@ function parseInput(){
40
39
  "command": "",
41
40
  "gdbversion": "SDE.DEFAULT",
42
41
  "file": "",
43
- "verify": "true"
42
+ "verify": "true",
43
+ "server": undefined
44
44
  }
45
45
 
46
46
  for (let i = 0; i < process.argv.length ; i++){
@@ -53,9 +53,10 @@ function parseInput(){
53
53
 
54
54
  if (Object.values(params).includes(null))
55
55
  {
56
- console.log ("HELP: uncli --portal https://unportal.domain.com/portal --service servicename --user username --password password [--gdbversion* user.version --file commandfile* --verify true|false]")
56
+ console.log ("HELP: uncli --portal https://unportal.domain.com/portal --service servicename --user username --password password [--gdbversion* user.version --server https://federatedserver.domain.com/server --file commandfile* --verify true|false]")
57
57
  console.log("--file commandfile is optional and you can pass a path to a file with a list of command to execute. ")
58
58
  console.log("--gdbversion is optional and allows the UN to be opened in that version. When not specified sde.DEFAULT is used.")
59
+ console.log("--server is optional except when there are more than one federated server sites to the portal. If the portal only has one server it will be selected.")
59
60
  process.exit();
60
61
  }
61
62
 
@@ -64,7 +65,7 @@ function parseInput(){
64
65
 
65
66
  //
66
67
  async function getToken(parameters) {
67
- portal = new Portal(parameters.portal, parameters.user, parameters.password)
68
+ portal = new Portal(parameters.portal, parameters.user, parameters.password,300, parameters.server)
68
69
  logger.info("About to connect..")
69
70
  const token = await portal.connect()
70
71
  logger.info(`Token generanted successfully.`)
@@ -177,8 +178,9 @@ const inputs = {
177
178
  "count": "Lists the number of rows in all feature layers.",
178
179
  "count --system": "Lists the number of rows in system layers.",
179
180
  "connect --service": "Connects to the another service",
181
+ "tracelogs --m <minutes>": "Lists utility network trace summary logs for the last x minutes (requires admin)",
180
182
  "arlogs": "Lists attribute rules execution logs (requires admin)",
181
- "arlogs --byrule": "Lists attribute rules execution summary by rule (requires admin)",
183
+ "arlogs --byrule [--minguid --maxguid]": "Lists attribute rules execution summary by rule (requires admin), --maxguid and --minguid show the GUID of the feature",
182
184
  "whoami": "Lists the current login info",
183
185
  "clear": "Clears this screen",
184
186
  "quit": "Exit this program"
@@ -713,7 +715,48 @@ const inputs = {
713
715
  },
714
716
 
715
717
 
716
- "^arlogs --byrule$": async () => {
718
+
719
+
720
+ "^tracelogs --m": async input => {
721
+ const topLogCount = 1000;
722
+ const pageSize = 10000
723
+
724
+ const inputParam = input.match(/--m .*/gm)
725
+ let mins = 30; //query logs for the last 30 minutes
726
+ if (inputParam != null && inputParam.length > 0)
727
+ mins = inputParam[0].replace("--m ", "")
728
+
729
+ console.log(`Querying trace logs for ${parameters.service} for the last ${mins} minutes ...`)
730
+ const startTime = Date.now() - mins*60*1000
731
+ const endTime = Date.now();
732
+ let result= await adminLog.query([102002], [parameters.service+ ".MapServer"], topLogCount, startTime ,endTime , "VERBOSE")
733
+ let jsonRes = await result.json()
734
+ let allMessages = [].concat(jsonRes.logMessages)
735
+
736
+ while (jsonRes.hasMore && allMessages.filter(m => m.message.indexOf(" Environment -") > -1).length < topLogCount )
737
+ {
738
+ //start paging
739
+ logger.info(`Aggregating messages... total so far ${allMessages.length} debug entries but more left, pulling logs before ${new Date(jsonRes.endTime)}`)
740
+ result= await adminLog.query([102002], [parameters.service + ".MapServer"], pageSize, jsonRes.endTime)
741
+ jsonRes = await result.json()
742
+ allMessages = allMessages.concat(jsonRes.logMessages)
743
+ }
744
+
745
+ allMessages.forEach(m => console.log(m.message))
746
+
747
+ },
748
+ "^arlogs --byrule": async input => {
749
+ //--minguid to show min guid
750
+ //--maxguid to show max guid
751
+ const inputParam = input.match(/--byrule .*/gm)
752
+ let showMaxGuid = false
753
+ let showMinGuid = false
754
+ if (inputParam != null && inputParam.length > 0 && inputParam[0].indexOf("--maxguid") > -1)
755
+ showMaxGuid = true
756
+
757
+ if (inputParam != null && inputParam.length > 0 && inputParam[0].indexOf("--minguid") > -1)
758
+ showMinGuid = true
759
+
717
760
  const pageSize = 10000 //maximum messages per page
718
761
  logger.info(`Querying attribute rules logs for ${parameters.service} ...`)
719
762
  let result= await adminLog.query([102003], [parameters.service + ".MapServer"], pageSize)
@@ -748,13 +791,31 @@ const inputs = {
748
791
  {
749
792
  prev [cur["Rule name"]] = {
750
793
  "totalTime": 0,
751
- "occurrence": 0
794
+ "occurrence": 0,
795
+ "minTime": Number.MAX_SAFE_INTEGER,
796
+ "maxTime": -1,
797
+ "avgTime": 0,
798
+ "maxGuid": null,
799
+ "minGuid": null
752
800
  };
753
801
  }
754
802
 
755
803
  prev [cur["Rule name"]].totalTime = prev [cur["Rule name"]].totalTime + cur["Elapsed Time (ms)"]
756
- prev [cur["Rule name"]].occurrence++
757
-
804
+
805
+ if (cur["Elapsed Time (ms)"] < prev [cur["Rule name"]].minTime ) {
806
+ prev [cur["Rule name"]].minTime = cur["Elapsed Time (ms)"];
807
+ prev [cur["Rule name"]].minGuid = cur["GlobalID"];
808
+ }
809
+
810
+ if (cur["Elapsed Time (ms)"] > prev [cur["Rule name"]].maxTime ){
811
+ prev [cur["Rule name"]].maxTime = cur["Elapsed Time (ms)"];
812
+ prev [cur["Rule name"]].maxGuid = cur["GlobalID"];
813
+ }
814
+
815
+ prev [cur["Rule name"]].occurrence++
816
+
817
+ prev [cur["Rule name"]].avgTime = prev [cur["Rule name"]].totalTime / prev [cur["Rule name"]].occurrence
818
+
758
819
  return prev
759
820
  }, {})
760
821
 
@@ -763,19 +824,26 @@ const inputs = {
763
824
  const rule = {}
764
825
  rule["Attribute Rule"] = a;
765
826
  rule["Total Cost (ms)"] = parseFloat(arMessages[a].totalTime.toFixed(2))
766
- rule["Occurrence"] = arMessages[a].occurrence;
827
+ if (!showMinGuid && !showMaxGuid) rule["Average Cost (ms)"] = parseFloat(arMessages[a].avgTime.toFixed(2))
828
+ rule["Max execution time (ms)"] = parseFloat(arMessages[a].maxTime.toFixed(2))
829
+ if (showMaxGuid) rule["Max GUID"] = arMessages[a].maxGuid
830
+ rule["Min execution time (ms)"] = parseFloat(arMessages[a].minTime.toFixed(2))
831
+ if (showMinGuid) rule["Min GUID"] = arMessages[a].minGuid
832
+ if (!showMinGuid && !showMaxGuid) rule["Occurrence"] = arMessages[a].occurrence;
767
833
  return rule;
768
834
  })
769
835
  .sort( (m1, m2) => m2["Total Cost (ms)"] -m1["Total Cost (ms)"])
770
836
  console.table(rules)
771
-
837
+
838
+ const totalARExecution = rules.reduce( (prev, cur) => prev + cur["Total Cost (ms)"], 0)
839
+ console.log(`Total time spend executing attribute rules (${Math.round(totalARExecution)} ms) (${Math.round(totalARExecution/1000)} s) (${Math.round(totalARExecution/(1000*60))} m)`)
772
840
 
773
841
  },
774
842
 
775
843
  "^version$": () => console.log(version),
776
844
  "^clear$|^cls$": () => console.clear(),
777
845
  "^quit$": () => {
778
- rl.close();
846
+ if (rl) rl.close();
779
847
  process.exit();
780
848
  },
781
849
  "^exit$|^quit$|^bye$": () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "un-cli",
3
- "version": "0.0.59",
3
+ "version": "0.0.62",
4
4
  "description": "Command line interface for working with ArcGIS Utility Network Extension",
5
5
  "main": "index.mjs",
6
6
  "bin": {
package/portal.mjs CHANGED
@@ -3,13 +3,13 @@ import { logger } from "./logger.mjs"
3
3
 
4
4
  export class Portal{
5
5
 
6
- constructor(url, username, password, expiration = 300)
6
+ constructor(url, username, password, expiration = 300, serverUrl = undefined)
7
7
  {
8
8
  this.url = url;
9
9
  this.username = username;
10
10
  this.password = password;
11
11
  this.expiration = expiration;
12
- this.serverUrl = "bad";
12
+ this.serverUrl = serverUrl;
13
13
  }
14
14
 
15
15
 
@@ -20,7 +20,6 @@ export class Portal{
20
20
 
21
21
  try {
22
22
 
23
- //https://utilitynetwork.esri.com/portal/sharing/rest/portals/self/servers?f=json
24
23
  const tokenUrl = self.url + "/sharing/rest/generateToken";
25
24
 
26
25
  const postJson = {
@@ -44,9 +43,9 @@ export class Portal{
44
43
 
45
44
  }
46
45
  catch(ex){
47
- logger.error(ex.status.message)
48
- console.error(ex.status.errno)
49
- reject(`Failed to connect to portal, check your username or password or add --verify false if you are using a self-signed certificate. Normally a production system should have a valid certificate signed by a CA and you should NOT disable verification in that case.)`)
46
+ logger.error(ex?.status?.message)
47
+ console.error(ex?.status?.errno)
48
+ reject(`Failed to connect to portal, check your username or password or add --verify false if you are using a self-signed certificate. Normally a production system should have a valid certificate signed by a CA and you should NOT disable verification in that case.) \n\n${ex}`)
50
49
  }
51
50
 
52
51
  }
@@ -55,32 +54,54 @@ export class Portal{
55
54
  }
56
55
 
57
56
  async updateServices () {
57
+ const self = this;
58
+ return new Promise( async (resolve, reject) => {
59
+ //if the user specified a serverUrl no need to do anything
60
+ if (self.serverUrl !== undefined) {
61
+ logger.info(`Using server ${self.serverUrl} supplied in the --server parameter`)
62
+ resolve(self.serverUrl);
63
+ return;
64
+ }
58
65
 
59
- try {
66
+ //else we need to calculate it
67
+ try {
60
68
 
61
- const postJsonServers= {
62
- f: "json",
63
- token: this.token
69
+ const postJsonServers= {
70
+ f: "json",
71
+ token: self.token
72
+ }
73
+
74
+ const serversUrl = self.url + "/sharing/rest/portals/self/servers"
75
+ logger.info( "About to query federated servers");
76
+
77
+ //query for federated servers.
78
+ const servers = await makeRequest({method: 'POST', url: serversUrl, params: postJsonServers });
79
+
80
+ //if we don't have any federated servers quit.
81
+ if (servers.servers.length === 0)
82
+ {
83
+ reject( "No federeated servers");
84
+ return
85
+ }
86
+
87
+ //if we have more than one then we let the user pick.
88
+ if (servers.servers.length > 1){
89
+ let serverUrls = "";
90
+ servers.servers.forEach(s => serverUrls += '\n * ' + s.url + '\n' )
91
+ reject("more than one federated server found, run the command with --server and specify one of the servers below\n" + serverUrls)
92
+ }
93
+ this.serverUrl = servers.servers[0].url;
94
+ resolve(self.serverUrl)
95
+ logger.info(`Found one federate server, using server url ${self.serverUrl} by default`)
64
96
  }
65
-
66
- const serversUrl = this.url + "/sharing/rest/portals/self/servers"
67
- logger.info( "About to query federated servers");
68
-
69
- //query for federated servers.
70
- const servers = await makeRequest({method: 'POST', url: serversUrl, params: postJsonServers });
71
-
72
- //get the first one
73
- if (servers.servers.length === 0)
74
- reject( "No federeated servers");
75
-
76
- this.serverUrl = servers.servers[0].url;
77
- logger.info(`Found server url ${this.serverUrl}`)
78
- }
79
- catch(ex){
80
- logger.error(ex)
81
- }
97
+ catch(ex){
98
+ logger.error(ex)
99
+ reject(ex)
100
+ }
101
+ });
102
+
103
+ }
82
104
 
83
- }
84
105
  //get the feature service definition
85
106
  async serviceDef(serviceName) {
86
107
  this.token = this.token;