From 23f2e28c176815b3428e38db627b36d7e8997ae0 Mon Sep 17 00:00:00 2001
From: janssen <118828444+janssen-tiobe@users.noreply.github.com>
Date: Tue, 27 Aug 2024 15:12:07 +0200
Subject: [PATCH 1/5] #34796: Fixed possible undefined toString calls
---
dist/index.js | 977 +++++++++++----------------
src/action/decorate/summary.ts | 12 +-
src/helper/interfaces.d.ts | 9 +-
src/tics/analyzer.ts | 16 +-
src/viewer/annotations.ts | 1 +
test/unit/viewer/annotations.test.ts | 4 +-
6 files changed, 422 insertions(+), 597 deletions(-)
diff --git a/dist/index.js b/dist/index.js
index 37f5612b..b04b360d 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -428,8 +428,15 @@ function createBody(annotation, displayCount) {
else if (annotation.blocking?.state === 'after' && annotation.blocking.after) {
body += `Blocking after: ${(0, date_fns_1.format)(annotation.blocking.after, 'yyyy-MM-dd')}${os_1.EOL}`;
}
+ const secondLine = [];
+ if (annotation.level) {
+ secondLine.push(`Level: ${annotation.level.toString()}`);
+ }
+ if (annotation.category) {
+ secondLine.push(`Category: ${annotation.category}`);
+ }
body += `Line: ${annotation.line.toString()}: ${displayCount}${annotation.msg}`;
- body += `${os_1.EOL}Level: ${annotation.level.toString()}, Category: ${annotation.category}`;
+ body += secondLine.length > 0 ? `${os_1.EOL}${secondLine.join(', ')}` : '';
body += annotation.ruleHelp ? `${os_1.EOL}Rule-help: ${annotation.ruleHelp}` : '';
return body;
}
@@ -525,7 +532,7 @@ function createUnpostableAnnotationsDetails(unpostableReviewComments) {
else if (previousPath !== path) {
body += `
${path} |
`;
}
- body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level.toString()} Category: ${reviewComment.category} | ${reviewComment.type} violation: ${reviewComment.rule} ${displayCount} ${reviewComment.msg} |
`;
+ body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''} Category: ${reviewComment.category ?? ''} | ${reviewComment.type} violation: ${reviewComment.rule} ${displayCount} ${reviewComment.msg} |
`;
previousPath = reviewComment.path ? reviewComment.path : '';
});
body += '
';
@@ -2254,17 +2261,17 @@ async function buildRunCommand(fileListPath) {
* @param data stdout or stderr
*/
function findInStdOutOrErr(data) {
- const error = data.toString().match(/\[ERROR.*/g);
- if (error && !errorList.find(e => e === error.toString())) {
- errorList.push(error.toString());
+ const error = data.match(/\[ERROR.*/g)?.toString();
+ if (error && !errorList.find(e => e === error)) {
+ errorList.push(error);
}
- const warning = data.toString().match(/\[WARNING.*/g);
- if (warning && !warningList.find(w => w === warning.toString())) {
- warningList.push(warning.toString());
+ const warning = data.match(/\[WARNING.*/g)?.toString();
+ if (warning && !warningList.find(w => w === warning)) {
+ warningList.push(warning);
}
- const noFilesToAnalyze = data.toString().match(/No files to analyze with option '-changed':/g);
+ const noFilesToAnalyze = data.match(/No files to analyze with option '-changed':/g);
if (noFilesToAnalyze) {
- warningList.push(`[WARNING 5057] ${data.toString()}`);
+ warningList.push(`[WARNING 5057] ${data}`);
}
const findExplorerUrl = data.match(/\/Explorer.*/g);
if (findExplorerUrl) {
@@ -2459,6 +2466,7 @@ async function getAnnotations(apiLinks) {
response.data.data.forEach((annotation) => {
const extendedAnnotation = {
...annotation,
+ count: annotation.count ?? 1,
instanceName: response.data.annotationTypes ? response.data.annotationTypes[annotation.type].instanceName : annotation.type
};
extendedAnnotation.gateId = index;
@@ -13493,16 +13501,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.InstallTics = void 0;
-exports.getBaseUrl = getBaseUrl;
+exports.InstallTics = exports.getBaseUrl = void 0;
const os = __importStar(__nccwpck_require__(22037));
-const http_client_1 = __importDefault(__nccwpck_require__(5098));
+const http_client_1 = __importDefault(__nccwpck_require__(14824));
var Platform;
(function (Platform) {
Platform["aix"] = "aix";
Platform["android"] = "android";
Platform["cygwin"] = "cygwin";
- Platform["darwin"] = "macos";
+ Platform["darwin"] = "darwin";
Platform["freebsd"] = "freebsd";
Platform["haiku"] = "haiku";
Platform["linux"] = "linux";
@@ -13530,6 +13537,7 @@ function getBaseUrl(url) {
}
throw Error('Incorrect TICS Viewer url was given.');
}
+exports.getBaseUrl = getBaseUrl;
class InstallTics {
/**
* @param ci If this http client is used in a ci environment.
@@ -13553,9 +13561,9 @@ class InstallTics {
const platform = Platform[os.platform()];
const installTicsUrl = yield this.getInstallTicsUrl(url, platform);
switch (platform) {
- case Platform.linux:
+ case 'linux':
return this.linuxInstall(installTicsUrl);
- case Platform.win32:
+ case 'windows':
return this.windowsInstall(installTicsUrl);
default:
throw Error(`No install command found for platform: ${platform}.`);
@@ -13617,199 +13625,6 @@ exports.InstallTics = InstallTics;
/***/ }),
-/***/ 5098:
-/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
-
-"use strict";
-
-var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- var desc = Object.getOwnPropertyDescriptor(m, k);
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
- desc = { enumerable: true, get: function() { return m[k]; } };
- }
- Object.defineProperty(o, k2, desc);
-}) : (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- o[k2] = m[k];
-}));
-var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
- Object.defineProperty(o, "default", { enumerable: true, value: v });
-}) : function(o, v) {
- o["default"] = v;
-});
-var __importStar = (this && this.__importStar) || function (mod) {
- if (mod && mod.__esModule) return mod;
- var result = {};
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
- __setModuleDefault(result, mod);
- return result;
-};
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
-};
-Object.defineProperty(exports, "__esModule", ({ value: true }));
-const node_fetch_1 = __importStar(__nccwpck_require__(80467));
-const retry_1 = __importStar(__nccwpck_require__(38480));
-class HttpClient {
- /**
- * @param ci If this http client is used in a ci environment.
- * @param options (Optional) options to use for the HttpClient.
- * @param agent (Optional) agent to be set when using proxy.
- */
- constructor(ci, options, agent) {
- this.ci = ci;
- this.agent = agent;
- this.defaultHeaders = new node_fetch_1.Headers();
- if (options === null || options === void 0 ? void 0 : options.authToken) {
- this.defaultHeaders.set('authorization', `Basic ${options.authToken}`);
- }
- if (options === null || options === void 0 ? void 0 : options.xRequestWithTics) {
- this.defaultHeaders.set('x-requested-with', 'TICS');
- }
- this.customFetch = (0, retry_1.default)(node_fetch_1.default, options === null || options === void 0 ? void 0 : options.retry);
- }
- /**
- * Executes a GET request to the given url.
- * @param url api url to perform a GET request for.
- */
- get(url, headers) {
- return __awaiter(this, void 0, void 0, function* () {
- let fixedHeaders = this.defaultHeaders;
- headers === null || headers === void 0 ? void 0 : headers.forEach((value, key) => {
- fixedHeaders.append(key, value);
- });
- const requestInit = {
- agent: this.agent,
- headers: fixedHeaders
- };
- const response = yield this.customFetch(url, requestInit);
- switch (response.status) {
- case 200:
- const text = yield response.text();
- try {
- const result = {
- status: response.status,
- retryCount: response.retryCount,
- data: JSON.parse(text)
- };
- return result;
- }
- catch (error) {
- throw new retry_1.RequestError(`${error}: ${text}`, response.status, response.retryCount);
- }
- case 302:
- throw new retry_1.RequestError(`HTTP request failed with status ${response.status}. Please check if the given ticsConfiguration is correct.`, response.status, response.retryCount);
- case 400:
- throw new retry_1.RequestError(`HTTP request failed with status ${response.status}. ${(yield response.json()).alertMessages[0].header}`, response.status, response.retryCount);
- case 401:
- let authUrl = url.split('/api/')[0];
- authUrl += this.ci ? '/Administration.html#page=authToken' : '/UserSettings.html#page=authToken';
- throw new retry_1.RequestError(`HTTP request failed with status ${response.status}. Please provide a valid TICS authentication token. See ${authUrl}`, response.status, response.retryCount);
- case 403:
- throw new retry_1.RequestError(`HTTP request failed with status ${response.status}. Forbidden call: ${url}`, response.status, response.retryCount);
- case 404:
- throw new retry_1.RequestError(`HTTP request failed with status ${response.status}. Please check if the given ticsConfiguration is correct.`, response.status, response.retryCount);
- default:
- throw new retry_1.RequestError(`HTTP request failed with status ${response.status}: ${response.statusText}`, response.status, response.retryCount);
- }
- });
- }
-}
-exports["default"] = HttpClient;
-//# sourceMappingURL=index.js.map
-
-/***/ }),
-
-/***/ 38480:
-/***/ ((__unused_webpack_module, exports) => {
-
-"use strict";
-
-Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.RequestError = void 0;
-exports.fetchBuilder = fetchBuilder;
-class RequestError extends Error {
- constructor(message, status, retryCount) {
- super(message);
- this.name = 'RequestError';
- this.status = status;
- this.retryCount = retryCount;
- }
-}
-exports.RequestError = RequestError;
-function sanitize(params, defaults) {
- const result = Object.assign(Object.assign({}, defaults), params);
- if (typeof result.retries === 'undefined') {
- result.retries = defaults.retries;
- }
- if (typeof result.retryDelay === 'undefined') {
- result.retryDelay = defaults.retryDelay;
- }
- if (typeof result.retryOn === 'undefined') {
- result.retryOn = defaults.retryOn;
- }
- return result;
-}
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function fetchBuilder(fetchFunc, params = {}) {
- const defaults = sanitize(params, { retries: 3, retryDelay: 500, retryOn: [419, 503, 504] });
- return function (input, init) {
- const frp = sanitize({
- // TICS -no-unsafe-assignment
- retries: init === null || init === void 0 ? void 0 : init.retries,
- retryDelay: init === null || init === void 0 ? void 0 : init.retryDelay,
- retryOn: init === null || init === void 0 ? void 0 : init.retryOn
- // TICS +no-unsafe-assignment
- }, defaults);
- const retryDelayFn = typeof frp.retryDelay === 'function' ? frp.retryDelay : () => frp.retryDelay;
- const retryOnFn = typeof frp.retryOn === 'function'
- ? frp.retryOn
- : (attempt, retries, error, response) => (!!error || !response || frp.retryOn.indexOf(response.status) !== -1) && attempt < retries;
- return new Promise(function (resolve, reject) {
- const extendedFetch = function (attempt) {
- fetchFunc(input, init)
- .then(function (response) {
- if (retryOnFn(attempt, frp.retries, null, response)) {
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
- retry(attempt, null, response);
- }
- else {
- const responseWithRetryCount = response;
- responseWithRetryCount.retryCount = attempt;
- resolve(responseWithRetryCount);
- }
- })
- .catch(function (error) {
- if (retryOnFn(attempt, frp.retries, error, null)) {
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
- retry(attempt, error, null);
- }
- else {
- reject(new RequestError(error.message, 0, attempt));
- }
- });
- };
- function retry(attempt, error, response) {
- setTimeout(function () {
- extendedFetch(++attempt);
- }, retryDelayFn(attempt, error, response));
- }
- extendedFetch(0);
- });
- };
-}
-exports["default"] = fetchBuilder;
-//# sourceMappingURL=retry.js.map
-
-/***/ }),
-
/***/ 40068:
/***/ ((__unused_webpack_module, exports) => {
@@ -41269,6 +41084,378 @@ function cleanEscapedString(input) {
}
+/***/ }),
+
+/***/ 93822:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+exports.parseISO = parseISO;
+var _index = __nccwpck_require__(17818);
+
+/**
+ * The {@link parseISO} function options.
+ */
+
+/**
+ * @name parseISO
+ * @category Common Helpers
+ * @summary Parse ISO string
+ *
+ * @description
+ * Parse the given string in ISO 8601 format and return an instance of Date.
+ *
+ * Function accepts complete ISO 8601 formats as well as partial implementations.
+ * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601
+ *
+ * If the argument isn't a string, the function cannot parse the string or
+ * the values are invalid, it returns Invalid Date.
+ *
+ * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
+ *
+ * @param argument - The value to convert
+ * @param options - An object with options
+ *
+ * @returns The parsed date in the local time zone
+ *
+ * @example
+ * // Convert string '2014-02-11T11:30:30' to date:
+ * const result = parseISO('2014-02-11T11:30:30')
+ * //=> Tue Feb 11 2014 11:30:30
+ *
+ * @example
+ * // Convert string '+02014101' to date,
+ * // if the additional number of digits in the extended year format is 1:
+ * const result = parseISO('+02014101', { additionalDigits: 1 })
+ * //=> Fri Apr 11 2014 00:00:00
+ */
+function parseISO(argument, options) {
+ const additionalDigits = options?.additionalDigits ?? 2;
+ const dateStrings = splitDateString(argument);
+
+ let date;
+ if (dateStrings.date) {
+ const parseYearResult = parseYear(dateStrings.date, additionalDigits);
+ date = parseDate(parseYearResult.restDateString, parseYearResult.year);
+ }
+
+ if (!date || isNaN(date.getTime())) {
+ return new Date(NaN);
+ }
+
+ const timestamp = date.getTime();
+ let time = 0;
+ let offset;
+
+ if (dateStrings.time) {
+ time = parseTime(dateStrings.time);
+ if (isNaN(time)) {
+ return new Date(NaN);
+ }
+ }
+
+ if (dateStrings.timezone) {
+ offset = parseTimezone(dateStrings.timezone);
+ if (isNaN(offset)) {
+ return new Date(NaN);
+ }
+ } else {
+ const dirtyDate = new Date(timestamp + time);
+ // JS parsed string assuming it's in UTC timezone
+ // but we need it to be parsed in our timezone
+ // so we use utc values to build date in our timezone.
+ // Year values from 0 to 99 map to the years 1900 to 1999
+ // so set year explicitly with setFullYear.
+ const result = new Date(0);
+ result.setFullYear(
+ dirtyDate.getUTCFullYear(),
+ dirtyDate.getUTCMonth(),
+ dirtyDate.getUTCDate(),
+ );
+ result.setHours(
+ dirtyDate.getUTCHours(),
+ dirtyDate.getUTCMinutes(),
+ dirtyDate.getUTCSeconds(),
+ dirtyDate.getUTCMilliseconds(),
+ );
+ return result;
+ }
+
+ return new Date(timestamp + time + offset);
+}
+
+const patterns = {
+ dateTimeDelimiter: /[T ]/,
+ timeZoneDelimiter: /[Z ]/i,
+ timezone: /([Z+-].*)$/,
+};
+
+const dateRegex =
+ /^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/;
+const timeRegex =
+ /^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/;
+const timezoneRegex = /^([+-])(\d{2})(?::?(\d{2}))?$/;
+
+function splitDateString(dateString) {
+ const dateStrings = {};
+ const array = dateString.split(patterns.dateTimeDelimiter);
+ let timeString;
+
+ // The regex match should only return at maximum two array elements.
+ // [date], [time], or [date, time].
+ if (array.length > 2) {
+ return dateStrings;
+ }
+
+ if (/:/.test(array[0])) {
+ timeString = array[0];
+ } else {
+ dateStrings.date = array[0];
+ timeString = array[1];
+ if (patterns.timeZoneDelimiter.test(dateStrings.date)) {
+ dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0];
+ timeString = dateString.substr(
+ dateStrings.date.length,
+ dateString.length,
+ );
+ }
+ }
+
+ if (timeString) {
+ const token = patterns.timezone.exec(timeString);
+ if (token) {
+ dateStrings.time = timeString.replace(token[1], "");
+ dateStrings.timezone = token[1];
+ } else {
+ dateStrings.time = timeString;
+ }
+ }
+
+ return dateStrings;
+}
+
+function parseYear(dateString, additionalDigits) {
+ const regex = new RegExp(
+ "^(?:(\\d{4}|[+-]\\d{" +
+ (4 + additionalDigits) +
+ "})|(\\d{2}|[+-]\\d{" +
+ (2 + additionalDigits) +
+ "})$)",
+ );
+
+ const captures = dateString.match(regex);
+ // Invalid ISO-formatted year
+ if (!captures) return { year: NaN, restDateString: "" };
+
+ const year = captures[1] ? parseInt(captures[1]) : null;
+ const century = captures[2] ? parseInt(captures[2]) : null;
+
+ // either year or century is null, not both
+ return {
+ year: century === null ? year : century * 100,
+ restDateString: dateString.slice((captures[1] || captures[2]).length),
+ };
+}
+
+function parseDate(dateString, year) {
+ // Invalid ISO-formatted year
+ if (year === null) return new Date(NaN);
+
+ const captures = dateString.match(dateRegex);
+ // Invalid ISO-formatted string
+ if (!captures) return new Date(NaN);
+
+ const isWeekDate = !!captures[4];
+ const dayOfYear = parseDateUnit(captures[1]);
+ const month = parseDateUnit(captures[2]) - 1;
+ const day = parseDateUnit(captures[3]);
+ const week = parseDateUnit(captures[4]);
+ const dayOfWeek = parseDateUnit(captures[5]) - 1;
+
+ if (isWeekDate) {
+ if (!validateWeekDate(year, week, dayOfWeek)) {
+ return new Date(NaN);
+ }
+ return dayOfISOWeekYear(year, week, dayOfWeek);
+ } else {
+ const date = new Date(0);
+ if (
+ !validateDate(year, month, day) ||
+ !validateDayOfYearDate(year, dayOfYear)
+ ) {
+ return new Date(NaN);
+ }
+ date.setUTCFullYear(year, month, Math.max(dayOfYear, day));
+ return date;
+ }
+}
+
+function parseDateUnit(value) {
+ return value ? parseInt(value) : 1;
+}
+
+function parseTime(timeString) {
+ const captures = timeString.match(timeRegex);
+ if (!captures) return NaN; // Invalid ISO-formatted time
+
+ const hours = parseTimeUnit(captures[1]);
+ const minutes = parseTimeUnit(captures[2]);
+ const seconds = parseTimeUnit(captures[3]);
+
+ if (!validateTime(hours, minutes, seconds)) {
+ return NaN;
+ }
+
+ return (
+ hours * _index.millisecondsInHour +
+ minutes * _index.millisecondsInMinute +
+ seconds * 1000
+ );
+}
+
+function parseTimeUnit(value) {
+ return (value && parseFloat(value.replace(",", "."))) || 0;
+}
+
+function parseTimezone(timezoneString) {
+ if (timezoneString === "Z") return 0;
+
+ const captures = timezoneString.match(timezoneRegex);
+ if (!captures) return 0;
+
+ const sign = captures[1] === "+" ? -1 : 1;
+ const hours = parseInt(captures[2]);
+ const minutes = (captures[3] && parseInt(captures[3])) || 0;
+
+ if (!validateTimezone(hours, minutes)) {
+ return NaN;
+ }
+
+ return (
+ sign *
+ (hours * _index.millisecondsInHour + minutes * _index.millisecondsInMinute)
+ );
+}
+
+function dayOfISOWeekYear(isoWeekYear, week, day) {
+ const date = new Date(0);
+ date.setUTCFullYear(isoWeekYear, 0, 4);
+ const fourthOfJanuaryDay = date.getUTCDay() || 7;
+ const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay;
+ date.setUTCDate(date.getUTCDate() + diff);
+ return date;
+}
+
+// Validation functions
+
+// February is null to handle the leap year (using ||)
+const daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+function isLeapYearIndex(year) {
+ return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);
+}
+
+function validateDate(year, month, date) {
+ return (
+ month >= 0 &&
+ month <= 11 &&
+ date >= 1 &&
+ date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28))
+ );
+}
+
+function validateDayOfYearDate(year, dayOfYear) {
+ return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365);
+}
+
+function validateWeekDate(_year, week, day) {
+ return week >= 1 && week <= 53 && day >= 0 && day <= 6;
+}
+
+function validateTime(hours, minutes, seconds) {
+ if (hours === 24) {
+ return minutes === 0 && seconds === 0;
+ }
+
+ return (
+ seconds >= 0 &&
+ seconds < 60 &&
+ minutes >= 0 &&
+ minutes < 60 &&
+ hours >= 0 &&
+ hours < 25
+ );
+}
+
+function validateTimezone(_hours, minutes) {
+ return minutes >= 0 && minutes <= 59;
+}
+
+
+/***/ }),
+
+/***/ 50135:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+exports.parseJSON = parseJSON; /**
+ * @name parseJSON
+ * @category Common Helpers
+ * @summary Parse a JSON date string
+ *
+ * @description
+ * Converts a complete ISO date string in UTC time, the typical format for transmitting
+ * a date in JSON, to a JavaScript `Date` instance.
+ *
+ * This is a minimal implementation for converting dates retrieved from a JSON API to
+ * a `Date` instance which can be used with other functions in the `date-fns` library.
+ * The following formats are supported:
+ *
+ * - `2000-03-15T05:20:10.123Z`: The output of `.toISOString()` and `JSON.stringify(new Date())`
+ * - `2000-03-15T05:20:10Z`: Without milliseconds
+ * - `2000-03-15T05:20:10+00:00`: With a zero offset, the default JSON encoded format in some other languages
+ * - `2000-03-15T05:20:10+05:45`: With a positive or negative offset, the default JSON encoded format in some other languages
+ * - `2000-03-15T05:20:10+0000`: With a zero offset without a colon
+ * - `2000-03-15T05:20:10`: Without a trailing 'Z' symbol
+ * - `2000-03-15T05:20:10.1234567`: Up to 7 digits in milliseconds field. Only first 3 are taken into account since JS does not allow fractional milliseconds
+ * - `2000-03-15 05:20:10`: With a space instead of a 'T' separator for APIs returning a SQL date without reformatting
+ *
+ * For convenience and ease of use these other input types are also supported
+ * via [toDate](https://date-fns.org/docs/toDate):
+ *
+ * - A `Date` instance will be cloned
+ * - A `number` will be treated as a timestamp
+ *
+ * Any other input type or invalid date strings will return an `Invalid Date`.
+ *
+ * @param dateStr - A fully formed ISO8601 date string to convert
+ *
+ * @returns The parsed date in the local time zone
+ */
+function parseJSON(dateStr) {
+ const parts = dateStr.match(
+ /(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d{0,7}))?(?:Z|(.)(\d{2}):?(\d{2})?)?/,
+ );
+ if (parts) {
+ // Group 8 matches the sign
+ return new Date(
+ Date.UTC(
+ +parts[1],
+ +parts[2] - 1,
+ +parts[3],
+ +parts[4] - (+parts[9] || 0) * (parts[8] == "-" ? -1 : 1),
+ +parts[5] - (+parts[10] || 0) * (parts[8] == "-" ? -1 : 1),
+ +parts[6],
+ +((parts[7] || "0") + "00").substring(0, 3),
+ ),
+ );
+ }
+ return new Date(NaN);
+}
+
+
/***/ }),
/***/ 5619:
@@ -43795,378 +43982,6 @@ function isLeapYearIndex(year) {
}
-/***/ }),
-
-/***/ 93822:
-/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
-
-"use strict";
-
-exports.parseISO = parseISO;
-var _index = __nccwpck_require__(17818);
-
-/**
- * The {@link parseISO} function options.
- */
-
-/**
- * @name parseISO
- * @category Common Helpers
- * @summary Parse ISO string
- *
- * @description
- * Parse the given string in ISO 8601 format and return an instance of Date.
- *
- * Function accepts complete ISO 8601 formats as well as partial implementations.
- * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601
- *
- * If the argument isn't a string, the function cannot parse the string or
- * the values are invalid, it returns Invalid Date.
- *
- * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
- *
- * @param argument - The value to convert
- * @param options - An object with options
- *
- * @returns The parsed date in the local time zone
- *
- * @example
- * // Convert string '2014-02-11T11:30:30' to date:
- * const result = parseISO('2014-02-11T11:30:30')
- * //=> Tue Feb 11 2014 11:30:30
- *
- * @example
- * // Convert string '+02014101' to date,
- * // if the additional number of digits in the extended year format is 1:
- * const result = parseISO('+02014101', { additionalDigits: 1 })
- * //=> Fri Apr 11 2014 00:00:00
- */
-function parseISO(argument, options) {
- const additionalDigits = options?.additionalDigits ?? 2;
- const dateStrings = splitDateString(argument);
-
- let date;
- if (dateStrings.date) {
- const parseYearResult = parseYear(dateStrings.date, additionalDigits);
- date = parseDate(parseYearResult.restDateString, parseYearResult.year);
- }
-
- if (!date || isNaN(date.getTime())) {
- return new Date(NaN);
- }
-
- const timestamp = date.getTime();
- let time = 0;
- let offset;
-
- if (dateStrings.time) {
- time = parseTime(dateStrings.time);
- if (isNaN(time)) {
- return new Date(NaN);
- }
- }
-
- if (dateStrings.timezone) {
- offset = parseTimezone(dateStrings.timezone);
- if (isNaN(offset)) {
- return new Date(NaN);
- }
- } else {
- const dirtyDate = new Date(timestamp + time);
- // JS parsed string assuming it's in UTC timezone
- // but we need it to be parsed in our timezone
- // so we use utc values to build date in our timezone.
- // Year values from 0 to 99 map to the years 1900 to 1999
- // so set year explicitly with setFullYear.
- const result = new Date(0);
- result.setFullYear(
- dirtyDate.getUTCFullYear(),
- dirtyDate.getUTCMonth(),
- dirtyDate.getUTCDate(),
- );
- result.setHours(
- dirtyDate.getUTCHours(),
- dirtyDate.getUTCMinutes(),
- dirtyDate.getUTCSeconds(),
- dirtyDate.getUTCMilliseconds(),
- );
- return result;
- }
-
- return new Date(timestamp + time + offset);
-}
-
-const patterns = {
- dateTimeDelimiter: /[T ]/,
- timeZoneDelimiter: /[Z ]/i,
- timezone: /([Z+-].*)$/,
-};
-
-const dateRegex =
- /^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/;
-const timeRegex =
- /^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/;
-const timezoneRegex = /^([+-])(\d{2})(?::?(\d{2}))?$/;
-
-function splitDateString(dateString) {
- const dateStrings = {};
- const array = dateString.split(patterns.dateTimeDelimiter);
- let timeString;
-
- // The regex match should only return at maximum two array elements.
- // [date], [time], or [date, time].
- if (array.length > 2) {
- return dateStrings;
- }
-
- if (/:/.test(array[0])) {
- timeString = array[0];
- } else {
- dateStrings.date = array[0];
- timeString = array[1];
- if (patterns.timeZoneDelimiter.test(dateStrings.date)) {
- dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0];
- timeString = dateString.substr(
- dateStrings.date.length,
- dateString.length,
- );
- }
- }
-
- if (timeString) {
- const token = patterns.timezone.exec(timeString);
- if (token) {
- dateStrings.time = timeString.replace(token[1], "");
- dateStrings.timezone = token[1];
- } else {
- dateStrings.time = timeString;
- }
- }
-
- return dateStrings;
-}
-
-function parseYear(dateString, additionalDigits) {
- const regex = new RegExp(
- "^(?:(\\d{4}|[+-]\\d{" +
- (4 + additionalDigits) +
- "})|(\\d{2}|[+-]\\d{" +
- (2 + additionalDigits) +
- "})$)",
- );
-
- const captures = dateString.match(regex);
- // Invalid ISO-formatted year
- if (!captures) return { year: NaN, restDateString: "" };
-
- const year = captures[1] ? parseInt(captures[1]) : null;
- const century = captures[2] ? parseInt(captures[2]) : null;
-
- // either year or century is null, not both
- return {
- year: century === null ? year : century * 100,
- restDateString: dateString.slice((captures[1] || captures[2]).length),
- };
-}
-
-function parseDate(dateString, year) {
- // Invalid ISO-formatted year
- if (year === null) return new Date(NaN);
-
- const captures = dateString.match(dateRegex);
- // Invalid ISO-formatted string
- if (!captures) return new Date(NaN);
-
- const isWeekDate = !!captures[4];
- const dayOfYear = parseDateUnit(captures[1]);
- const month = parseDateUnit(captures[2]) - 1;
- const day = parseDateUnit(captures[3]);
- const week = parseDateUnit(captures[4]);
- const dayOfWeek = parseDateUnit(captures[5]) - 1;
-
- if (isWeekDate) {
- if (!validateWeekDate(year, week, dayOfWeek)) {
- return new Date(NaN);
- }
- return dayOfISOWeekYear(year, week, dayOfWeek);
- } else {
- const date = new Date(0);
- if (
- !validateDate(year, month, day) ||
- !validateDayOfYearDate(year, dayOfYear)
- ) {
- return new Date(NaN);
- }
- date.setUTCFullYear(year, month, Math.max(dayOfYear, day));
- return date;
- }
-}
-
-function parseDateUnit(value) {
- return value ? parseInt(value) : 1;
-}
-
-function parseTime(timeString) {
- const captures = timeString.match(timeRegex);
- if (!captures) return NaN; // Invalid ISO-formatted time
-
- const hours = parseTimeUnit(captures[1]);
- const minutes = parseTimeUnit(captures[2]);
- const seconds = parseTimeUnit(captures[3]);
-
- if (!validateTime(hours, minutes, seconds)) {
- return NaN;
- }
-
- return (
- hours * _index.millisecondsInHour +
- minutes * _index.millisecondsInMinute +
- seconds * 1000
- );
-}
-
-function parseTimeUnit(value) {
- return (value && parseFloat(value.replace(",", "."))) || 0;
-}
-
-function parseTimezone(timezoneString) {
- if (timezoneString === "Z") return 0;
-
- const captures = timezoneString.match(timezoneRegex);
- if (!captures) return 0;
-
- const sign = captures[1] === "+" ? -1 : 1;
- const hours = parseInt(captures[2]);
- const minutes = (captures[3] && parseInt(captures[3])) || 0;
-
- if (!validateTimezone(hours, minutes)) {
- return NaN;
- }
-
- return (
- sign *
- (hours * _index.millisecondsInHour + minutes * _index.millisecondsInMinute)
- );
-}
-
-function dayOfISOWeekYear(isoWeekYear, week, day) {
- const date = new Date(0);
- date.setUTCFullYear(isoWeekYear, 0, 4);
- const fourthOfJanuaryDay = date.getUTCDay() || 7;
- const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay;
- date.setUTCDate(date.getUTCDate() + diff);
- return date;
-}
-
-// Validation functions
-
-// February is null to handle the leap year (using ||)
-const daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
-
-function isLeapYearIndex(year) {
- return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);
-}
-
-function validateDate(year, month, date) {
- return (
- month >= 0 &&
- month <= 11 &&
- date >= 1 &&
- date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28))
- );
-}
-
-function validateDayOfYearDate(year, dayOfYear) {
- return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365);
-}
-
-function validateWeekDate(_year, week, day) {
- return week >= 1 && week <= 53 && day >= 0 && day <= 6;
-}
-
-function validateTime(hours, minutes, seconds) {
- if (hours === 24) {
- return minutes === 0 && seconds === 0;
- }
-
- return (
- seconds >= 0 &&
- seconds < 60 &&
- minutes >= 0 &&
- minutes < 60 &&
- hours >= 0 &&
- hours < 25
- );
-}
-
-function validateTimezone(_hours, minutes) {
- return minutes >= 0 && minutes <= 59;
-}
-
-
-/***/ }),
-
-/***/ 50135:
-/***/ ((__unused_webpack_module, exports) => {
-
-"use strict";
-
-exports.parseJSON = parseJSON; /**
- * @name parseJSON
- * @category Common Helpers
- * @summary Parse a JSON date string
- *
- * @description
- * Converts a complete ISO date string in UTC time, the typical format for transmitting
- * a date in JSON, to a JavaScript `Date` instance.
- *
- * This is a minimal implementation for converting dates retrieved from a JSON API to
- * a `Date` instance which can be used with other functions in the `date-fns` library.
- * The following formats are supported:
- *
- * - `2000-03-15T05:20:10.123Z`: The output of `.toISOString()` and `JSON.stringify(new Date())`
- * - `2000-03-15T05:20:10Z`: Without milliseconds
- * - `2000-03-15T05:20:10+00:00`: With a zero offset, the default JSON encoded format in some other languages
- * - `2000-03-15T05:20:10+05:45`: With a positive or negative offset, the default JSON encoded format in some other languages
- * - `2000-03-15T05:20:10+0000`: With a zero offset without a colon
- * - `2000-03-15T05:20:10`: Without a trailing 'Z' symbol
- * - `2000-03-15T05:20:10.1234567`: Up to 7 digits in milliseconds field. Only first 3 are taken into account since JS does not allow fractional milliseconds
- * - `2000-03-15 05:20:10`: With a space instead of a 'T' separator for APIs returning a SQL date without reformatting
- *
- * For convenience and ease of use these other input types are also supported
- * via [toDate](https://date-fns.org/docs/toDate):
- *
- * - A `Date` instance will be cloned
- * - A `number` will be treated as a timestamp
- *
- * Any other input type or invalid date strings will return an `Invalid Date`.
- *
- * @param dateStr - A fully formed ISO8601 date string to convert
- *
- * @returns The parsed date in the local time zone
- */
-function parseJSON(dateStr) {
- const parts = dateStr.match(
- /(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d{0,7}))?(?:Z|(.)(\d{2}):?(\d{2})?)?/,
- );
- if (parts) {
- // Group 8 matches the sign
- return new Date(
- Date.UTC(
- +parts[1],
- +parts[2] - 1,
- +parts[3],
- +parts[4] - (+parts[9] || 0) * (parts[8] == "-" ? -1 : 1),
- +parts[5] - (+parts[10] || 0) * (parts[8] == "-" ? -1 : 1),
- +parts[6],
- +((parts[7] || "0") + "00").substring(0, 3),
- ),
- );
- }
- return new Date(NaN);
-}
-
-
/***/ }),
/***/ 456:
diff --git a/src/action/decorate/summary.ts b/src/action/decorate/summary.ts
index 535c464d..e8d90922 100644
--- a/src/action/decorate/summary.ts
+++ b/src/action/decorate/summary.ts
@@ -278,8 +278,16 @@ function createBody(annotation: Annotation, displayCount: string) {
body += `Blocking after: ${format(annotation.blocking.after, 'yyyy-MM-dd')}${EOL}`;
}
+ const secondLine: string[] = [];
+ if (annotation.level) {
+ secondLine.push(`Level: ${annotation.level.toString()}`);
+ }
+ if (annotation.category) {
+ secondLine.push(`Category: ${annotation.category}`);
+ }
+
body += `Line: ${annotation.line.toString()}: ${displayCount}${annotation.msg}`;
- body += `${EOL}Level: ${annotation.level.toString()}, Category: ${annotation.category}`;
+ body += secondLine.length > 0 ? `${EOL}${secondLine.join(', ')}` : '';
body += annotation.ruleHelp ? `${EOL}Rule-help: ${annotation.ruleHelp}` : '';
return body;
@@ -385,7 +393,7 @@ export function createUnpostableAnnotationsDetails(unpostableReviewComments: Ext
} else if (previousPath !== path) {
body += `${path} |
`;
}
- body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level.toString()} Category: ${reviewComment.category} | ${reviewComment.type} violation: ${reviewComment.rule} ${displayCount} ${reviewComment.msg} |
`;
+ body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''} Category: ${reviewComment.category ?? ''} | ${reviewComment.type} violation: ${reviewComment.rule} ${displayCount} ${reviewComment.msg} |
`;
previousPath = reviewComment.path ? reviewComment.path : '';
});
body += '
';
diff --git a/src/helper/interfaces.d.ts b/src/helper/interfaces.d.ts
index 8d10af12..e18a343a 100644
--- a/src/helper/interfaces.d.ts
+++ b/src/helper/interfaces.d.ts
@@ -151,15 +151,14 @@ export interface AnnotationType {
export interface Annotation {
fullPath: string;
line: number;
- level: number;
- category: string;
+ level?: number;
+ category?: string;
rule: string;
msg: string;
supp: boolean;
type: string;
- count: number;
+ count?: number;
gateId?: number;
- displayCount?: string;
path?: string;
diffLines?: number[];
ruleHelp?: string;
@@ -171,6 +170,8 @@ export interface Annotation {
}
export interface ExtendedAnnotation extends Annotation {
+ count: number;
+ displayCount?: string;
instanceName: string;
}
diff --git a/src/tics/analyzer.ts b/src/tics/analyzer.ts
index 3ffce544..35268f26 100644
--- a/src/tics/analyzer.ts
+++ b/src/tics/analyzer.ts
@@ -97,19 +97,19 @@ async function buildRunCommand(fileListPath: string): Promise {
* @param data stdout or stderr
*/
function findInStdOutOrErr(data: string): void {
- const error = data.toString().match(/\[ERROR.*/g);
- if (error && !errorList.find(e => e === error.toString())) {
- errorList.push(error.toString());
+ const error = data.match(/\[ERROR.*/g)?.toString();
+ if (error && !errorList.find(e => e === error)) {
+ errorList.push(error);
}
- const warning = data.toString().match(/\[WARNING.*/g);
- if (warning && !warningList.find(w => w === warning.toString())) {
- warningList.push(warning.toString());
+ const warning = data.match(/\[WARNING.*/g)?.toString();
+ if (warning && !warningList.find(w => w === warning)) {
+ warningList.push(warning);
}
- const noFilesToAnalyze = data.toString().match(/No files to analyze with option '-changed':/g);
+ const noFilesToAnalyze = data.match(/No files to analyze with option '-changed':/g);
if (noFilesToAnalyze) {
- warningList.push(`[WARNING 5057] ${data.toString()}`);
+ warningList.push(`[WARNING 5057] ${data}`);
}
const findExplorerUrl = data.match(/\/Explorer.*/g);
diff --git a/src/viewer/annotations.ts b/src/viewer/annotations.ts
index 875e05e1..8eac7f21 100644
--- a/src/viewer/annotations.ts
+++ b/src/viewer/annotations.ts
@@ -32,6 +32,7 @@ export async function getAnnotations(apiLinks: AnnotationApiLink[]): Promise {
const extendedAnnotation: ExtendedAnnotation = {
...annotation,
+ count: annotation.count ?? 1,
instanceName: response.data.annotationTypes ? response.data.annotationTypes[annotation.type].instanceName : annotation.type
};
extendedAnnotation.gateId = index;
diff --git a/test/unit/viewer/annotations.test.ts b/test/unit/viewer/annotations.test.ts
index 208a5b7f..e8241566 100644
--- a/test/unit/viewer/annotations.test.ts
+++ b/test/unit/viewer/annotations.test.ts
@@ -17,8 +17,8 @@ describe('getAnnotations', () => {
const response = await getAnnotations([{ url: 'url?fields=default,blocking' }, { url: 'url' }]);
expect(response).toEqual([
- { type: 'CS', gateId: 0, instanceName: 'CS' },
- { type: 'CS', gateId: 1, instanceName: 'Coding Standard Violations' }
+ { type: 'CS', gateId: 0, count: 1, instanceName: 'CS' },
+ { type: 'CS', gateId: 1, count: 1, instanceName: 'Coding Standard Violations' }
]);
});
From 2cffd43315d489f5979fb7385c51a21dc6e0086a Mon Sep 17 00:00:00 2001
From: janssen <118828444+janssen-tiobe@users.noreply.github.com>
Date: Tue, 27 Aug 2024 15:29:17 +0200
Subject: [PATCH 2/5] #34796: Added case for when line is undefined (with
notice)
---
dist/index.js | 5 +++++
src/action/decorate/summary.ts | 2 +-
src/helper/interfaces.d.ts | 3 ++-
src/viewer/annotations.ts | 6 ++++++
test/unit/viewer/annotations.test.ts | 4 ++--
5 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/dist/index.js b/dist/index.js
index b04b360d..dd6f3fae 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -2464,8 +2464,13 @@ async function getAnnotations(apiLinks) {
try {
const response = await http_client_1.httpClient.get(annotationsUrl.href);
response.data.data.forEach((annotation) => {
+ if (!annotation.line) {
+ annotation.line = 1;
+ logger_1.logger.notice(`No line number reported for ${annotation.rule} in file ${annotation.fullPath}. Reporting the annotation on line 1.`);
+ }
const extendedAnnotation = {
...annotation,
+ line: annotation.line,
count: annotation.count ?? 1,
instanceName: response.data.annotationTypes ? response.data.annotationTypes[annotation.type].instanceName : annotation.type
};
diff --git a/src/action/decorate/summary.ts b/src/action/decorate/summary.ts
index e8d90922..1792ee7e 100644
--- a/src/action/decorate/summary.ts
+++ b/src/action/decorate/summary.ts
@@ -270,7 +270,7 @@ export function createReviewComments(annotations: ExtendedAnnotation[], changedF
return { postable: postable, unpostable: unpostable };
}
-function createBody(annotation: Annotation, displayCount: string) {
+function createBody(annotation: ExtendedAnnotation, displayCount: string) {
let body = '';
if (annotation.blocking?.state === 'yes') {
body += `Blocking${EOL}`;
diff --git a/src/helper/interfaces.d.ts b/src/helper/interfaces.d.ts
index e18a343a..77eb255b 100644
--- a/src/helper/interfaces.d.ts
+++ b/src/helper/interfaces.d.ts
@@ -150,7 +150,7 @@ export interface AnnotationType {
export interface Annotation {
fullPath: string;
- line: number;
+ line?: number;
level?: number;
category?: string;
rule: string;
@@ -170,6 +170,7 @@ export interface Annotation {
}
export interface ExtendedAnnotation extends Annotation {
+ line: number;
count: number;
displayCount?: string;
instanceName: string;
diff --git a/src/viewer/annotations.ts b/src/viewer/annotations.ts
index 8eac7f21..a0b4dfa8 100644
--- a/src/viewer/annotations.ts
+++ b/src/viewer/annotations.ts
@@ -30,8 +30,14 @@ export async function getAnnotations(apiLinks: AnnotationApiLink[]): Promise(annotationsUrl.href);
response.data.data.forEach((annotation: Annotation) => {
+ if (!annotation.line) {
+ annotation.line = 1;
+ logger.notice(`No line number reported for ${annotation.rule} in file ${annotation.fullPath}. Reporting the annotation on line 1.`);
+ }
+
const extendedAnnotation: ExtendedAnnotation = {
...annotation,
+ line: annotation.line,
count: annotation.count ?? 1,
instanceName: response.data.annotationTypes ? response.data.annotationTypes[annotation.type].instanceName : annotation.type
};
diff --git a/test/unit/viewer/annotations.test.ts b/test/unit/viewer/annotations.test.ts
index e8241566..70ac6d7c 100644
--- a/test/unit/viewer/annotations.test.ts
+++ b/test/unit/viewer/annotations.test.ts
@@ -17,8 +17,8 @@ describe('getAnnotations', () => {
const response = await getAnnotations([{ url: 'url?fields=default,blocking' }, { url: 'url' }]);
expect(response).toEqual([
- { type: 'CS', gateId: 0, count: 1, instanceName: 'CS' },
- { type: 'CS', gateId: 1, count: 1, instanceName: 'Coding Standard Violations' }
+ { type: 'CS', gateId: 0, line: 1, count: 1, instanceName: 'CS' },
+ { type: 'CS', gateId: 1, line: 1, count: 1, instanceName: 'Coding Standard Violations' }
]);
});
From da01e24d0758977f566ec17b135c0d8d5ab904c5 Mon Sep 17 00:00:00 2001
From: janssen <118828444+janssen-tiobe@users.noreply.github.com>
Date: Tue, 27 Aug 2024 17:32:45 +0200
Subject: [PATCH 3/5] #34796: Fixed coding standards violation
---
src/action/decorate/summary.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/action/decorate/summary.ts b/src/action/decorate/summary.ts
index 1792ee7e..8a586552 100644
--- a/src/action/decorate/summary.ts
+++ b/src/action/decorate/summary.ts
@@ -10,7 +10,6 @@ import { logger } from '../../helper/logger';
import { joinUrl } from '../../helper/url';
import {
AnalysisResult,
- Annotation,
Condition,
ConditionDetails,
ExtendedAnnotation,
From 0f3348ceeb3b762a8f1edc3d97495d5b9b9c1684 Mon Sep 17 00:00:00 2001
From: janssen <118828444+janssen-tiobe@users.noreply.github.com>
Date: Wed, 28 Aug 2024 10:43:34 +0200
Subject: [PATCH 4/5] #34794: Added fix for missing message in case of
complexity
Also removed displaying rule if undefined
---
dist/index.js | 12 +++--
src/action/decorate/summary.ts | 5 +-
src/helper/interfaces.d.ts | 5 +-
src/viewer/annotations.ts | 8 ++-
test/unit/action/decorate/summary.test.ts | 4 +-
test/unit/viewer/annotations.test.ts | 60 +++++++++++++++++++----
6 files changed, 76 insertions(+), 18 deletions(-)
diff --git a/dist/index.js b/dist/index.js
index dd6f3fae..d262921c 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -382,11 +382,12 @@ function createReviewComments(annotations, changedFiles) {
.filter(a => a.blocking?.state !== 'no')
.forEach(annotation => {
const displayCount = annotation.count === 1 ? '' : `(${annotation.count.toString()}x) `;
+ const title = annotation.instanceName + (annotation.rule ? `: ${annotation.rule}` : '');
if (config_1.githubConfig.event.isPullRequest) {
if (changedFiles.find(c => annotation.fullPath.includes(c.filename))) {
const reviewComment = {
blocking: annotation.blocking?.state,
- title: `${annotation.instanceName}: ${annotation.rule}`,
+ title: title,
body: createBody(annotation, displayCount),
path: annotation.path,
line: annotation.line
@@ -403,7 +404,7 @@ function createReviewComments(annotations, changedFiles) {
else if (annotation.diffLines?.includes(annotation.line)) {
const reviewComment = {
blocking: annotation.blocking?.state,
- title: `${annotation.instanceName}: ${annotation.rule}`,
+ title: title,
body: createBody(annotation, displayCount),
path: annotation.path,
line: annotation.line
@@ -2468,13 +2469,18 @@ async function getAnnotations(apiLinks) {
annotation.line = 1;
logger_1.logger.notice(`No line number reported for ${annotation.rule} in file ${annotation.fullPath}. Reporting the annotation on line 1.`);
}
+ // In case complexity is given, the annotation does not have a message (should be fixed in newer Viewers ).
+ // Present in Viewers <= 2024.2.0
+ if (annotation.complexity && !annotation.msg) {
+ annotation.msg = `Function ${annotation.functionName} has a complexity of ${annotation.complexity.toString()}`;
+ }
const extendedAnnotation = {
...annotation,
+ gateId: index,
line: annotation.line,
count: annotation.count ?? 1,
instanceName: response.data.annotationTypes ? response.data.annotationTypes[annotation.type].instanceName : annotation.type
};
- extendedAnnotation.gateId = index;
logger_1.logger.debug(JSON.stringify(extendedAnnotation));
annotations.push(extendedAnnotation);
});
diff --git a/src/action/decorate/summary.ts b/src/action/decorate/summary.ts
index 8a586552..fd0eb7c5 100644
--- a/src/action/decorate/summary.ts
+++ b/src/action/decorate/summary.ts
@@ -232,12 +232,13 @@ export function createReviewComments(annotations: ExtendedAnnotation[], changedF
.filter(a => a.blocking?.state !== 'no')
.forEach(annotation => {
const displayCount = annotation.count === 1 ? '' : `(${annotation.count.toString()}x) `;
+ const title = annotation.instanceName + (annotation.rule ? `: ${annotation.rule}` : '');
if (githubConfig.event.isPullRequest) {
if (changedFiles.find(c => annotation.fullPath.includes(c.filename))) {
const reviewComment = {
blocking: annotation.blocking?.state,
- title: `${annotation.instanceName}: ${annotation.rule}`,
+ title: title,
body: createBody(annotation, displayCount),
path: annotation.path,
line: annotation.line
@@ -252,7 +253,7 @@ export function createReviewComments(annotations: ExtendedAnnotation[], changedF
} else if (annotation.diffLines?.includes(annotation.line)) {
const reviewComment = {
blocking: annotation.blocking?.state,
- title: `${annotation.instanceName}: ${annotation.rule}`,
+ title: title,
body: createBody(annotation, displayCount),
path: annotation.path,
line: annotation.line
diff --git a/src/helper/interfaces.d.ts b/src/helper/interfaces.d.ts
index 77eb255b..958a2017 100644
--- a/src/helper/interfaces.d.ts
+++ b/src/helper/interfaces.d.ts
@@ -153,7 +153,7 @@ export interface Annotation {
line?: number;
level?: number;
category?: string;
- rule: string;
+ rule?: string;
msg: string;
supp: boolean;
type: string;
@@ -167,9 +167,12 @@ export interface Annotation {
state: 'yes' | 'no' | 'after';
after?: number;
};
+ complexity?: number;
+ functionName?: string;
}
export interface ExtendedAnnotation extends Annotation {
+ msg: string;
line: number;
count: number;
displayCount?: string;
diff --git a/src/viewer/annotations.ts b/src/viewer/annotations.ts
index a0b4dfa8..b0e41fba 100644
--- a/src/viewer/annotations.ts
+++ b/src/viewer/annotations.ts
@@ -35,13 +35,19 @@ export async function getAnnotations(apiLinks: AnnotationApiLink[]): Promise {
}
},
{
+ // testing one without a rule
fullPath: 'c:/src/test.js',
line: 0,
level: 1,
category: 'test',
type: 'test',
- rule: 'rule-after',
msg: 'message-after',
count: 1,
supp: false,
@@ -338,7 +338,7 @@ describe('createReviewComments', () => {
},
{
blocking: 'after',
- title: 'test: rule-after',
+ title: 'test',
path: 'src/test.js',
line: 0,
body: `Blocking after: 2024-02-19${EOL}Line: 0: message-after${EOL}Level: 1, Category: test`
diff --git a/test/unit/viewer/annotations.test.ts b/test/unit/viewer/annotations.test.ts
index 70ac6d7c..d5eb45c6 100644
--- a/test/unit/viewer/annotations.test.ts
+++ b/test/unit/viewer/annotations.test.ts
@@ -5,24 +5,66 @@ import { ticsConfigMock } from '../../.setup/mock';
describe('getAnnotations', () => {
ticsConfigMock.baseUrl = 'http://base.url';
- test('Should return analyzed files from viewer', async () => {
+ test('Should return annotations from viewer', async () => {
jest.spyOn(httpClient, 'get').mockImplementationOnce((): Promise => Promise.resolve({ data: { data: [{ type: 'CS' }] } }));
- jest
- .spyOn(httpClient, 'get')
- .mockImplementationOnce(
- (): Promise =>
- Promise.resolve({ data: { data: [{ type: 'CS' }], annotationTypes: { CS: { instanceName: 'Coding Standard Violations' } } } })
- );
+ jest.spyOn(httpClient, 'get').mockImplementationOnce(
+ (): Promise =>
+ Promise.resolve({
+ data: {
+ data: [{ type: 'CS', line: 10, count: 2 }],
+ annotationTypes: { CS: { instanceName: 'Coding Standard Violations' } }
+ }
+ })
+ );
const response = await getAnnotations([{ url: 'url?fields=default,blocking' }, { url: 'url' }]);
expect(response).toEqual([
{ type: 'CS', gateId: 0, line: 1, count: 1, instanceName: 'CS' },
- { type: 'CS', gateId: 1, line: 1, count: 1, instanceName: 'Coding Standard Violations' }
+ { type: 'CS', gateId: 1, line: 10, count: 2, instanceName: 'Coding Standard Violations' }
]);
});
- test('Should return no analyzed files when no urls are given', async () => {
+ test('Should return complexity annotations from the viewer', async () => {
+ jest.spyOn(httpClient, 'get').mockImplementationOnce(
+ (): Promise =>
+ Promise.resolve({
+ data: {
+ data: [
+ { type: 'COMPLEXITY', line: 10, complexity: 3, functionName: 'main' },
+ { type: 'COMPLEXITY', line: 2, complexity: 2, functionName: 'test', msg: 'testing' }
+ ]
+ }
+ })
+ );
+
+ const response = await getAnnotations([{ url: 'url' }]);
+
+ expect(response).toEqual([
+ {
+ type: 'COMPLEXITY',
+ complexity: 3,
+ functionName: 'main',
+ gateId: 0,
+ line: 10,
+ count: 1,
+ instanceName: 'COMPLEXITY',
+ msg: 'Function main has a complexity of 3'
+ },
+ {
+ type: 'COMPLEXITY',
+ complexity: 2,
+ functionName: 'test',
+ gateId: 0,
+ line: 2,
+ count: 1,
+ instanceName: 'COMPLEXITY',
+ msg: 'testing'
+ }
+ ]);
+ });
+
+ test('Should return no annotations when no urls are given', async () => {
const response = await getAnnotations([]);
expect(response).toEqual([]);
From 17368cd3e8834a088ca97313c5e1025337c30f97 Mon Sep 17 00:00:00 2001
From: janssen <118828444+janssen-tiobe@users.noreply.github.com>
Date: Wed, 28 Aug 2024 10:59:31 +0200
Subject: [PATCH 5/5] #34794: Added ticket number for message fix, qg fixes
---
dist/index.js | 9 +++++----
src/action/decorate/summary.ts | 2 +-
src/viewer/annotations.ts | 9 ++++++---
3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/dist/index.js b/dist/index.js
index d262921c..888344a5 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -533,7 +533,7 @@ function createUnpostableAnnotationsDetails(unpostableReviewComments) {
else if (previousPath !== path) {
body += `${path} |
`;
}
- body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''} Category: ${reviewComment.category ?? ''} | ${reviewComment.type} violation: ${reviewComment.rule} ${displayCount} ${reviewComment.msg} |
`;
+ body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''} Category: ${reviewComment.category ?? ''} | ${reviewComment.type} violation: ${reviewComment.rule ?? ''} ${displayCount} ${reviewComment.msg} |
`;
previousPath = reviewComment.path ? reviewComment.path : '';
});
body += '
';
@@ -2467,12 +2467,13 @@ async function getAnnotations(apiLinks) {
response.data.data.forEach((annotation) => {
if (!annotation.line) {
annotation.line = 1;
- logger_1.logger.notice(`No line number reported for ${annotation.rule} in file ${annotation.fullPath}. Reporting the annotation on line 1.`);
+ const rule = annotation.rule ? ` ${annotation.rule} ` : ' ';
+ logger_1.logger.notice(`No line number reported for ${annotation.type} violation${rule}in file ${annotation.fullPath}. Reporting the annotation on line 1.`);
}
- // In case complexity is given, the annotation does not have a message (should be fixed in newer Viewers ).
+ // In case complexity is given, the annotation does not have a message (should be fixed in newer Viewers #34866).
// Present in Viewers <= 2024.2.0
if (annotation.complexity && !annotation.msg) {
- annotation.msg = `Function ${annotation.functionName} has a complexity of ${annotation.complexity.toString()}`;
+ annotation.msg = `Function ${annotation.functionName ?? ''} has a complexity of ${annotation.complexity.toString()}`;
}
const extendedAnnotation = {
...annotation,
diff --git a/src/action/decorate/summary.ts b/src/action/decorate/summary.ts
index fd0eb7c5..95ba7181 100644
--- a/src/action/decorate/summary.ts
+++ b/src/action/decorate/summary.ts
@@ -393,7 +393,7 @@ export function createUnpostableAnnotationsDetails(unpostableReviewComments: Ext
} else if (previousPath !== path) {
body += `${path} |
`;
}
- body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''} Category: ${reviewComment.category ?? ''} | ${reviewComment.type} violation: ${reviewComment.rule} ${displayCount} ${reviewComment.msg} |
`;
+ body += `${icon} | ${blocking} | Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''} Category: ${reviewComment.category ?? ''} | ${reviewComment.type} violation: ${reviewComment.rule ?? ''} ${displayCount} ${reviewComment.msg} |
`;
previousPath = reviewComment.path ? reviewComment.path : '';
});
body += '
';
diff --git a/src/viewer/annotations.ts b/src/viewer/annotations.ts
index b0e41fba..81c72aa3 100644
--- a/src/viewer/annotations.ts
+++ b/src/viewer/annotations.ts
@@ -32,13 +32,16 @@ export async function getAnnotations(apiLinks: AnnotationApiLink[]): Promise {
if (!annotation.line) {
annotation.line = 1;
- logger.notice(`No line number reported for ${annotation.rule} in file ${annotation.fullPath}. Reporting the annotation on line 1.`);
+ const rule = annotation.rule ? ` ${annotation.rule} ` : ' ';
+ logger.notice(
+ `No line number reported for ${annotation.type} violation${rule}in file ${annotation.fullPath}. Reporting the annotation on line 1.`
+ );
}
- // In case complexity is given, the annotation does not have a message (should be fixed in newer Viewers ).
+ // In case complexity is given, the annotation does not have a message (should be fixed in newer Viewers #34866).
// Present in Viewers <= 2024.2.0
if (annotation.complexity && !annotation.msg) {
- annotation.msg = `Function ${annotation.functionName} has a complexity of ${annotation.complexity.toString()}`;
+ annotation.msg = `Function ${annotation.functionName ?? ''} has a complexity of ${annotation.complexity.toString()}`;
}
const extendedAnnotation: ExtendedAnnotation = {