|
| 1 | +// @ts-check |
| 2 | + |
| 3 | +// Ported from @azure/swagger-validation-common:src/types/message.ts |
| 4 | + |
| 5 | +import { markdownTable } from "markdown-table"; |
| 6 | +import * as z from "zod"; |
| 7 | + |
| 8 | +/** |
| 9 | + * @readonly |
| 10 | + * @enum {"Info" | "Warning" | "Error"} |
| 11 | + */ |
| 12 | +export const MessageLevel = Object.freeze({ |
| 13 | + Info: "Info", |
| 14 | + Warning: "Warning", |
| 15 | + Error: "Error", |
| 16 | +}); |
| 17 | +/** @type {import("zod").ZodType<MessageLevel>} */ |
| 18 | +export const MessageLevelSchema = z.enum(Object.values(MessageLevel)); |
| 19 | + |
| 20 | +export const MessageContextSchema = z.object({ |
| 21 | + toolVersion: z.string(), |
| 22 | +}); |
| 23 | +/** |
| 24 | + * @typedef {import("zod").infer<typeof MessageContextSchema>} MessageContext |
| 25 | + */ |
| 26 | + |
| 27 | +export const ExtraSchema = z.record(z.string(), z.any()); |
| 28 | +/** |
| 29 | + * @typedef {import("zod").infer<typeof ExtraSchema>} Extra |
| 30 | + */ |
| 31 | + |
| 32 | +export const BaseMessageRecordSchema = z.object({ |
| 33 | + level: MessageLevelSchema, |
| 34 | + message: z.string(), |
| 35 | + time: z.iso.datetime(), |
| 36 | + context: z.optional(MessageContextSchema), |
| 37 | + group: z.optional(z.string()), |
| 38 | + extra: z.optional(ExtraSchema), |
| 39 | + groupName: z.optional(z.string()), |
| 40 | +}); |
| 41 | +/** |
| 42 | + * @typedef {import("zod").infer<typeof BaseMessageRecordSchema>} BaseMessageRecord |
| 43 | + */ |
| 44 | + |
| 45 | +/** |
| 46 | + * @readonly |
| 47 | + * @enum {"Raw" | "Result"} |
| 48 | + */ |
| 49 | +export const MessageType = Object.freeze({ |
| 50 | + Raw: "Raw", |
| 51 | + Result: "Result", |
| 52 | +}); |
| 53 | +/** @type {import("zod").ZodType<MessageType>} */ |
| 54 | +export const MessageTypeSchema = z.enum(Object.values(MessageType)); |
| 55 | + |
| 56 | +export const RawMessageRecordSchema = BaseMessageRecordSchema.extend({ |
| 57 | + type: z.literal(MessageType.Raw), |
| 58 | +}); |
| 59 | +/** |
| 60 | + * @typedef {import("zod").infer<typeof RawMessageRecordSchema>} RawMessageRecord |
| 61 | + */ |
| 62 | + |
| 63 | +export const JsonPathSchema = z.object({ |
| 64 | + tag: z.string(), |
| 65 | + path: z.string(), |
| 66 | + jsonPath: z.optional(z.string()), |
| 67 | +}); |
| 68 | +/** |
| 69 | + * @typedef {import("zod").infer<typeof JsonPathSchema>} JsonPathSchema |
| 70 | + */ |
| 71 | + |
| 72 | +export const ResultMessageRecordSchema = BaseMessageRecordSchema.extend({ |
| 73 | + type: z.literal(MessageType.Result), |
| 74 | + id: z.optional(z.string()), |
| 75 | + code: z.optional(z.string()), |
| 76 | + docUrl: z.optional(z.string()), |
| 77 | + paths: z.array(JsonPathSchema), |
| 78 | +}); |
| 79 | +/** |
| 80 | + * @typedef {import("zod").infer<typeof ResultMessageRecordSchema>} ResultMessageRecord |
| 81 | + */ |
| 82 | + |
| 83 | +export const MessageRecordSchema = z.discriminatedUnion("type", [ |
| 84 | + RawMessageRecordSchema, |
| 85 | + ResultMessageRecordSchema, |
| 86 | +]); |
| 87 | +/** |
| 88 | + * @typedef {import("zod").infer<typeof MessageRecordSchema>} MessageRecord |
| 89 | + */ |
| 90 | + |
| 91 | +/** |
| 92 | + * Adds table of messages to core.summary |
| 93 | + * |
| 94 | + * @param {MessageRecord[]} messages |
| 95 | + */ |
| 96 | +export function generateMarkdownTable(messages) { |
| 97 | + const header = ["Rule", "Message"]; |
| 98 | + const rows = messages.map((m) => getMarkdownRow(m)); |
| 99 | + return markdownTable([header, ...rows]); |
| 100 | +} |
| 101 | + |
| 102 | +/** |
| 103 | + * @param {MessageRecord} record |
| 104 | + * @returns {string[]} |
| 105 | + */ |
| 106 | +function getMarkdownRow(record) { |
| 107 | + if (record.type === MessageType.Result) { |
| 108 | + return [ |
| 109 | + getLevelMarkdown(record) + " " + getRuleMarkdown(record), |
| 110 | + getMessageMarkdown(record) + "<br>" + getLocationMarkdown(record), |
| 111 | + ]; |
| 112 | + } else { |
| 113 | + return [getLevelMarkdown(record) + " " + getMessageMarkdown(record), getExtraMarkdown(record)]; |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +// Following ported from openapi-alps/reportGenerator.ts |
| 118 | + |
| 119 | +/** |
| 120 | + * @param {MessageRecord} record |
| 121 | + * @returns {string} |
| 122 | + */ |
| 123 | +function getLevelMarkdown(record) { |
| 124 | + switch (record.level) { |
| 125 | + case "Error": |
| 126 | + return "❌"; |
| 127 | + case "Info": |
| 128 | + return "ℹ️"; |
| 129 | + case "Warning": |
| 130 | + return "⚠️"; |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +/** |
| 135 | + * @param {ResultMessageRecord} result |
| 136 | + * @returns {string} |
| 137 | + */ |
| 138 | +function getRuleMarkdown(result) { |
| 139 | + let ruleName = [result.id, result.code].filter((s) => s).join(" - "); |
| 140 | + return `[${ruleName}](${result.docUrl})`; |
| 141 | +} |
| 142 | + |
| 143 | +/** |
| 144 | + * @param {ResultMessageRecord} result |
| 145 | + * @returns {string} |
| 146 | + */ |
| 147 | +function getLocationMarkdown(result) { |
| 148 | + return result.paths |
| 149 | + .filter((p) => p.path) |
| 150 | + .map((p) => `${p.tag}: [${getPathSegment(p.path)}](${p.path})`) |
| 151 | + .join("<br>"); |
| 152 | +} |
| 153 | + |
| 154 | +/** |
| 155 | + * @param {string} path |
| 156 | + * @returns {string} |
| 157 | + */ |
| 158 | +function getPathSegment(path) { |
| 159 | + const idx = path.indexOf("path="); |
| 160 | + if (idx !== -1) { |
| 161 | + path = decodeURIComponent(path.substr(idx + 5).split("&")[0]); |
| 162 | + } |
| 163 | + // for github url |
| 164 | + return path.split("/").slice(-4).join("/").split("#")[0]; |
| 165 | +} |
| 166 | + |
| 167 | +/** |
| 168 | + * @param {MessageRecord} record |
| 169 | + * @returns {string} |
| 170 | + */ |
| 171 | +function getMessageMarkdown(record) { |
| 172 | + if (record.type === MessageType.Raw) { |
| 173 | + return record.message.replace(/\\n\\n/g, "\n").split("\n")[0]; |
| 174 | + } else { |
| 175 | + // record.type === MessageType.Raw |
| 176 | + const re = /(\n|\t|\r)/gi; |
| 177 | + return record.message.replace(re, " "); |
| 178 | + } |
| 179 | +} |
| 180 | +/** |
| 181 | + * @param {MessageRecord} record |
| 182 | + * @returns {string} |
| 183 | + */ |
| 184 | +function getExtraMarkdown(record) { |
| 185 | + return JSON.stringify(record.extra || {}) |
| 186 | + .replace(/[{}]/g, "") |
| 187 | + .replace(/,/g, ",<br>"); |
| 188 | +} |
0 commit comments