@appland/scanner 1.71.7 → 1.73.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ # [@appland/scanner-v1.73.0](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.72.0...@appland/scanner-v1.73.0) (2022-11-28)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Address PR comments ([788dab4](https://github.com/getappmap/appmap-js/commit/788dab46cea2933dc0c5965f6307f5c0ef4d2873))
7
+ * sendTelemetry uses ScanResults.aggregate ([14e5bbb](https://github.com/getappmap/appmap-js/commit/14e5bbb074e4153878875a9376cbf10330baa1b4))
8
+
9
+
10
+ ### Features
11
+
12
+ * Send scan:started at beginning of analysis and send data in ([76d2141](https://github.com/getappmap/appmap-js/commit/76d2141fdadabeb21f15af86f879e76ff0389237))
13
+
14
+ # [@appland/scanner-v1.72.0](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.71.7...@appland/scanner-v1.72.0) (2022-11-01)
15
+
16
+
17
+ ### Features
18
+
19
+ * Generate sequence diagram ([870be30](https://github.com/getappmap/appmap-js/commit/870be30dbdc074672cc43495eebd396fd97c0184))
20
+
1
21
  # [@appland/scanner-v1.71.7](https://github.com/getappmap/appmap-js/compare/@appland/scanner-v1.71.6...@appland/scanner-v1.71.7) (2022-10-21)
2
22
 
3
23
 
@@ -20,11 +20,18 @@ const findingsReport_1 = __importDefault(require("../../report/findingsReport"))
20
20
  const summaryReport_1 = __importDefault(require("../../report/summaryReport"));
21
21
  const formatReport_1 = require("./formatReport");
22
22
  const telemetry_1 = __importDefault(require("../../telemetry"));
23
+ const scanResults_1 = require("../../report/scanResults");
23
24
  const util_1 = require("../../rules/lib/util");
24
25
  const validateFile_1 = __importDefault(require("../validateFile"));
25
26
  function singleScan(options) {
26
27
  return __awaiter(this, void 0, void 0, function* () {
27
28
  const { appmapFile, appmapDir, configuration, reportAllFindings, appId, ide, reportFile } = options;
29
+ telemetry_1.default.sendEvent({
30
+ name: 'scan:started',
31
+ properties: {
32
+ ide,
33
+ },
34
+ });
28
35
  const skipErrors = appmapDir !== undefined;
29
36
  const files = yield (0, util_1.collectAppMapFiles)(appmapFile, appmapDir);
30
37
  yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () { return (0, validateFile_1.default)('file', file); })));
@@ -52,22 +59,12 @@ function singleScan(options) {
52
59
  const elapsed = Date.now() - startTime;
53
60
  const numChecks = scanResults.checks.length * scanResults.summary.numAppMaps;
54
61
  console.log(`Performed ${numChecks} checks in ${elapsed}ms (${Math.floor(numChecks / (elapsed / 1000.0))} checks/sec)`);
55
- sendTelemetry(scanResults, elapsed);
56
- });
57
- }
58
- exports.default = singleScan;
59
- function sendTelemetry(scanResults, msElapsed) {
60
- const rules = [...new Set(scanResults.checks.map(({ id }) => id))];
61
- telemetry_1.default.sendEvent({
62
- name: 'scan:completed',
63
- properties: {
64
- rules: rules.join(', '),
65
- },
66
- metrics: {
67
- duration: msElapsed / 1000,
68
- numRules: rules.length,
62
+ (0, scanResults_1.sendScanResultsTelemetry)({
63
+ ruleIds: scanResults.summary.rules,
69
64
  numAppMaps: scanResults.summary.numAppMaps,
70
- numFindings: scanResults.findings.length,
71
- },
65
+ numFindings: scanResults.summary.numFindings,
66
+ elapsedMs: elapsed,
67
+ });
72
68
  });
73
69
  }
70
+ exports.default = singleScan;
@@ -38,12 +38,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.Watcher = void 0;
39
39
  const promises_1 = require("fs/promises");
40
40
  const chokidar = __importStar(require("chokidar"));
41
- const formatReport_1 = require("./formatReport");
42
- const scanner_1 = __importDefault(require("./scanner"));
43
- const configurationProvider_1 = require("../../configuration/configurationProvider");
44
41
  const assert_1 = __importDefault(require("assert"));
45
42
  const path_1 = __importDefault(require("path"));
46
43
  const async_1 = require("async");
44
+ const formatReport_1 = require("./formatReport");
45
+ const scanner_1 = __importDefault(require("./scanner"));
46
+ const configurationProvider_1 = require("../../configuration/configurationProvider");
47
+ const telemetry_1 = __importDefault(require("../../telemetry"));
48
+ const events_1 = __importDefault(require("events"));
47
49
  function isDir(targetPath) {
48
50
  return __awaiter(this, void 0, void 0, function* () {
49
51
  try {
@@ -70,11 +72,15 @@ function isAncestorPath(ancestor, descendant) {
70
72
  class Watcher {
71
73
  constructor(options) {
72
74
  this.options = options;
75
+ this.scanEventEmitter = new events_1.default();
73
76
  this.queue = (0, async_1.queue)(this.scan.bind(this), 2);
74
77
  }
75
78
  watch() {
76
79
  return __awaiter(this, void 0, void 0, function* () {
77
80
  yield this.reloadConfig();
81
+ telemetry_1.default.sendEvent({
82
+ name: 'scan:started',
83
+ });
78
84
  this.configWatcher = chokidar.watch(this.options.configFile, {
79
85
  ignoreInitial: true,
80
86
  });
@@ -140,13 +146,16 @@ class Watcher {
140
146
  reportStats.mtimeMs > appmapStats.mtimeMs &&
141
147
  reportStats.mtimeMs > this.config.timestampMs)
142
148
  return; // report is up to date
149
+ const startTime = Date.now();
143
150
  const scanner = yield (0, scanner_1.default)(true, this.config, [appmapFile]);
144
151
  const rawScanResults = yield scanner.scan();
152
+ const elapsed = Date.now() - startTime;
153
+ this.scanEventEmitter.emit('scan', { scanResults: rawScanResults, elapsed });
145
154
  // Always report the raw data
146
155
  yield (0, promises_1.writeFile)(reportFile, (0, formatReport_1.formatReport)(rawScanResults));
147
156
  // tell the queue that the current task is complete
148
157
  if (callback) {
149
- yield callback();
158
+ callback();
150
159
  }
151
160
  });
152
161
  }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.WatchScanTelemetry = void 0;
7
+ const eventAggregator_1 = __importDefault(require("../../lib/eventAggregator"));
8
+ const scanResults_1 = require("../../report/scanResults");
9
+ class WatchScanTelemetry {
10
+ constructor(scanEvents) {
11
+ new eventAggregator_1.default((events) => {
12
+ const scanEvents = events.map((e) => e.arg);
13
+ this.sendTelemetry(scanEvents);
14
+ }).attach(scanEvents, 'scan');
15
+ }
16
+ sendTelemetry(scanEvents) {
17
+ const ruleIds = new Set();
18
+ let elapsed = 0;
19
+ const telemetryScanResults = new scanResults_1.ScanResults();
20
+ for (const scanEvent of scanEvents) {
21
+ telemetryScanResults.aggregate(scanEvent.scanResults);
22
+ elapsed += scanEvent.elapsed;
23
+ }
24
+ telemetryScanResults.summary.rules.forEach((rule) => ruleIds.add(rule));
25
+ (0, scanResults_1.sendScanResultsTelemetry)({
26
+ ruleIds: [...ruleIds],
27
+ numAppMaps: telemetryScanResults.summary.numAppMaps,
28
+ numFindings: telemetryScanResults.summary.numFindings,
29
+ elapsedMs: elapsed,
30
+ });
31
+ }
32
+ }
33
+ exports.WatchScanTelemetry = WatchScanTelemetry;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MaxMSBetween = void 0;
4
+ exports.MaxMSBetween = 10 * 1000;
5
+ // TODO: Unify with the code in packages/cli - find a way to make a common import.
6
+ class EventAggregator {
7
+ constructor(callback, maxMsBetween = exports.MaxMSBetween) {
8
+ this.callback = callback;
9
+ this.maxMsBetween = maxMsBetween;
10
+ this.pending = [];
11
+ process.on('exit', () => {
12
+ if (this.timeout) {
13
+ clearTimeout(this.timeout);
14
+ this.emitPending();
15
+ }
16
+ });
17
+ }
18
+ push(emitter, event, arg) {
19
+ this.pending.push({ emitter, event, arg });
20
+ this.refresh();
21
+ }
22
+ refresh() {
23
+ if (this.timeout)
24
+ clearTimeout(this.timeout);
25
+ this.timeout = setTimeout(this.emitPending.bind(this), this.maxMsBetween);
26
+ }
27
+ emitPending() {
28
+ this.callback(this.pending);
29
+ this.timeout = undefined;
30
+ this.pending = [];
31
+ }
32
+ attach(emitter, event) {
33
+ emitter.on(event, (...args) => this.push(emitter, event, args[0]));
34
+ }
35
+ }
36
+ exports.default = EventAggregator;
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ScanResults = void 0;
6
+ exports.sendScanResultsTelemetry = exports.ScanResults = void 0;
7
+ const telemetry_1 = __importDefault(require("../telemetry"));
4
8
  class DistinctItems {
5
9
  constructor() {
6
10
  this.members = {};
@@ -55,7 +59,7 @@ function collectMetadata(metadata) {
55
59
  * It's used for printing a user-friendly summary report, it's not used for machine-readable program output.
56
60
  */
57
61
  class ScanResults {
58
- constructor(configuration, appMapMetadata, findings, checks) {
62
+ constructor(configuration = { checks: [] }, appMapMetadata = {}, findings = [], checks = []) {
59
63
  this.configuration = configuration;
60
64
  this.appMapMetadata = appMapMetadata;
61
65
  this.findings = findings;
@@ -72,5 +76,31 @@ class ScanResults {
72
76
  withFindings(findings) {
73
77
  return new ScanResults(this.configuration, this.appMapMetadata, findings, this.checks);
74
78
  }
79
+ aggregate(sourceScanResults) {
80
+ this.summary.numAppMaps += sourceScanResults.summary.numAppMaps;
81
+ this.summary.numChecks += sourceScanResults.summary.numChecks;
82
+ this.summary.rules = [...new Set(this.summary.rules.concat(sourceScanResults.summary.rules))];
83
+ this.summary.ruleLabels = [
84
+ ...new Set(this.summary.ruleLabels.concat(sourceScanResults.summary.ruleLabels)),
85
+ ];
86
+ this.summary.numFindings += sourceScanResults.summary.numFindings;
87
+ // we don't need sourceScanResults.summary.appMetadata
88
+ // appMapMetadata.Git may also contain secrets we don't want to transmit.
89
+ }
75
90
  }
76
91
  exports.ScanResults = ScanResults;
92
+ function sendScanResultsTelemetry(telemetry) {
93
+ telemetry_1.default.sendEvent({
94
+ name: 'scan:completed',
95
+ properties: {
96
+ rules: telemetry.ruleIds.sort().join(', '),
97
+ },
98
+ metrics: {
99
+ duration: telemetry.elapsedMs / 1000,
100
+ numRules: telemetry.ruleIds.length,
101
+ numAppMaps: telemetry.numAppMaps,
102
+ numFindings: telemetry.numFindings,
103
+ },
104
+ });
105
+ }
106
+ exports.sendScanResultsTelemetry = sendScanResultsTelemetry;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@appland/scanner",
3
- "version": "1.71.7",
4
- "description": "",
3
+ "version": "1.73.0",
4
+ "description": "Analyze AppMaps for code flaws",
5
5
  "bin": "built/cli.js",
6
6
  "files": [
7
7
  "built",
@@ -61,7 +61,7 @@
61
61
  "dependencies": {
62
62
  "@appland/client": "^1.3.0",
63
63
  "@appland/models": "^1.18.1",
64
- "@appland/openapi": "1.0.3",
64
+ "@appland/openapi": "1.2.0",
65
65
  "@appland/sql-parser": "^1.5.0",
66
66
  "@types/cli-progress": "^3.9.2",
67
67
  "ajv": "^8.8.2",