|
1 | 1 | import path from 'path'; |
2 | 2 | import fs from 'fs'; |
3 | 3 | import core from '@actions/core'; |
4 | | -import github from '@actions/github'; |
5 | 4 |
|
6 | | -import axios from 'axios' |
7 | | -import UmamiApiClient from 'umami-api' |
| 5 | +import UmamiClient from 'umami-api-client' |
8 | 6 |
|
9 | 7 | import ReportGenerator from './reportGenerator.js'; |
| 8 | +import axios from "axios"; |
10 | 9 |
|
11 | 10 | const DEBUG_ACTION = process.env.UMAMI_DEBUG_ACTION === 'true'; |
12 | 11 | const UMAMI_OUTPUT_DIRECTORY = process.env.UMAMI_OUTPUT_DIRECTORY || './umami'; |
13 | | -const rethrow = (err) => {throw err;} |
| 12 | +const rethrow = (err) => { |
| 13 | + throw new Error(err); |
| 14 | +} |
14 | 15 |
|
15 | 16 | class Action { |
16 | 17 |
|
17 | | - static async fetchUmamiServerApi(server, timeoutMs = 50000) { |
18 | | - const username = 'abc'; |
19 | | - const password = 'abc'; |
20 | | - console.log(`fetchUmamiServerApi`); |
21 | | - for (var i = 1 ; i < 10 ; i++) { |
22 | | - var expectedResult = []; |
23 | | - const action = "post " + i; |
24 | | - console.time(action); |
25 | | - const client = axios.create({ baseURL: `${server}/api`, timeout: timeoutMs }); |
26 | | - const loginResult = await client.post("/auth/login", { username, password }).catch(error => { |
27 | | - console.timeEnd(action); |
28 | | - const message = typeof error.response !== "undefined" ? error.response.data : error.message; |
29 | | - const status = typeof error.response !== "undefined" ? `${error.response.status} ${error.response.statusText}`:''; |
30 | | - const logMessage = status !== message ? `[${status}] ${message}` : message; |
31 | | - if (logMessage !== '401 Unauthorized') { |
32 | | - console.log(`Login failed: ${logMessage}`); |
33 | | - } else { |
34 | | - expectedResult.push(error); |
| 18 | + /** |
| 19 | + * fetch umami api to understand flaky results |
| 20 | + * cf. https://github.com/boly38/action-umami-report/issues/37 |
| 21 | + * @param server |
| 22 | + * @param timeoutMs |
| 23 | + * @returns {Promise<unknown>} |
| 24 | + */ |
| 25 | + static async fetchUmamiServerApi(server, timeoutMs = 50000) { |
| 26 | + return new Promise(async (resolve, reject) => { |
| 27 | + const username = 'admin'; |
| 28 | + const password = '075827845F'; |
| 29 | + console.log(`fetchUmamiServerApi`); |
| 30 | + let fetchResults = []; |
| 31 | + for (let i = 1; i < 10; i++) { |
| 32 | + const action = "post " + i; |
| 33 | + console.time(action); |
| 34 | + const client = axios.create({baseURL: `${server}/api`, timeout: timeoutMs}); |
| 35 | + const loginResult = await client.post("/auth/login", {username, password}).catch(error => { |
| 36 | + console.timeEnd(action); |
| 37 | + const message = typeof error.response !== "undefined" ? error.response.data : error.message; |
| 38 | + const status = typeof error.response !== "undefined" ? `${error.response.status} ${error.response.statusText}` : ''; |
| 39 | + const logMessage = status !== message ? `[${status}] ${message}` : message; |
| 40 | + if (logMessage !== '401 Unauthorized') { |
| 41 | + console.log(`Login failed: ${logMessage}`); |
| 42 | + } |
| 43 | + fetchResults.push(status); |
| 44 | + }); |
| 45 | + if (loginResult !== undefined) { |
| 46 | + fetchResults.push(`OK ${JSON.stringify(loginResult.status)}`); |
| 47 | + } |
35 | 48 | } |
| 49 | + resolve(fetchResults); |
36 | 50 | }); |
37 | | - if (loginResult !== undefined || expectedResult.length > 0) { |
38 | | - return; |
39 | | - } |
40 | | - } |
41 | | - } |
42 | | - |
43 | | - static async produceActionResult(resultName, resultValue, outputFile = null) { |
44 | | - const outFileAddition = outputFile !== null ? `file:${outputFile}` : ''; |
45 | | - console.info(`produce action result (output): ${resultName} ${outFileAddition}`); |
46 | | - core.setOutput(resultName, resultValue);// core action output: to be declared as outputs by current job and used by another job |
47 | | - // disabled by #12 // core.exportVariable(resultName, resultValue);// core action env: to be used by current job by another step |
48 | | - |
49 | | - var targetFile = null; |
50 | | - if (isSet(UMAMI_OUTPUT_DIRECTORY) && isSet(outputFile)) { |
51 | | - targetFile = `${UMAMI_OUTPUT_DIRECTORY}/${outputFile}`; |
52 | | - try { |
53 | | - ensureDirectoryExistence(targetFile); |
54 | | - fs.writeFileSync(targetFile, resultValue); |
55 | | - } catch (error) { |
56 | | - console.error(`ERROR: unable to write to ${targetFile} : ${error}`); |
57 | | - targetFile = null; |
58 | | - } |
59 | | - } |
60 | | - return { targetFile }; |
61 | | - } |
62 | | - |
63 | | - |
64 | | - static async produceReport(umamiSite, umamiSiteStats, sitePageViews = null, siteEvents = null, siteMetricsUrl = null, |
65 | | - outputFile = null, reportContent = 'pageviews|events|urls', period = '24h', unit = 'hour') { |
66 | | - //~~ generate umami report content |
67 | | - const generator = new ReportGenerator(umamiSite, reportContent, period, unit, umamiSiteStats, sitePageViews, siteEvents, siteMetricsUrl); |
68 | | - const umamiOneLineReport = generator.oneLineReport(); |
69 | | - const umamiReport = generator.detailedReport(); |
70 | | - |
71 | | - //~~ produce github actions results (output) |
72 | | - Action.produceActionResult("pageViews", umamiSiteStats.pageviews.value, null); |
73 | | - Action.produceActionResult("umamiOneLineReport", umamiOneLineReport); |
74 | | - const { targetFile } = await Action.produceActionResult("umamiReport", umamiReport, outputFile); |
75 | | - Action.produceActionResult("umamiReportLength", umamiReport.length, null);// #14 |
76 | | - if ( isSet(targetFile) ) { |
77 | | - Action.produceActionResult("umamiReportFile", targetFile, null); |
78 | | - return targetFile; |
79 | | - } |
80 | | - return null; |
81 | | - } |
82 | | - |
83 | | - /** |
84 | | - * @deprecated : please use umamiReport(...) |
85 | | - */ |
86 | | - static async umamiDailyReportV0(server, user, password, domain = '', outputFile = null, reportContent = 'pageviews|events|urls') { |
87 | | - var options = { server, user, password, domain, outputFile, reportContent }; |
88 | | - return await Action.umamiReport(options); |
89 | | - } |
90 | | - |
91 | | - static async umamiReport(options) { |
92 | | - // options |
93 | | - var { server, user, password, domain, outputFile, reportContent, period, unit, tz } = options; |
94 | | - // default options |
95 | | - if (!isSet(user)) { |
96 | | - user = 'admin'; |
97 | 51 | } |
98 | | - if (!isSet(outputFile)) { |
99 | | - outputFile = null; |
100 | | - } |
101 | | - if (!isSet(reportContent)) { |
102 | | - reportContent = 'pageviews|events|urls'; |
103 | | - } |
104 | | - if (!isSet(period)) { |
105 | | - period = '24h'; |
106 | | - } |
107 | | - if (!isSet(unit)) { |
108 | | - unit = 'hour'; |
| 52 | + |
| 53 | + static async produceActionResult(resultName, resultValue, outputFile = null) { |
| 54 | + const outFileAddition = outputFile !== null ? `file:${outputFile}` : ''; |
| 55 | + console.info(`produce action result (output): ${resultName} ${outFileAddition}`); |
| 56 | + core.setOutput(resultName, resultValue);// core action output: to be declared as outputs by current job and used by another job |
| 57 | + // disabled by #12 // core.exportVariable(resultName, resultValue);// core action env: to be used by current job by another step |
| 58 | + |
| 59 | + let targetFile = null; |
| 60 | + if (isSet(UMAMI_OUTPUT_DIRECTORY) && isSet(outputFile)) { |
| 61 | + targetFile = `${UMAMI_OUTPUT_DIRECTORY}/${outputFile}`; |
| 62 | + try { |
| 63 | + ensureDirectoryExistence(targetFile); |
| 64 | + fs.writeFileSync(targetFile, resultValue); |
| 65 | + } catch (error) { |
| 66 | + console.info(`ERROR: unable to write to ${targetFile} : ${error}`); |
| 67 | + targetFile = null; |
| 68 | + } |
| 69 | + } |
| 70 | + if (isSet(targetFile)) { |
| 71 | + console.info(`produce action result (targetFile): ${targetFile}`); |
| 72 | + return {targetFile}; |
| 73 | + } |
| 74 | + return {}; |
109 | 75 | } |
110 | | - if (!isSet(tz)) { |
111 | | - tz = 'Europe/Paris'; |
| 76 | + |
| 77 | + |
| 78 | + static async produceReport(umamiSite, umamiSiteStats, sitePageViews = null, siteEvents = null, siteMetricsUrl = null, |
| 79 | + outputFile = null, reportContent = 'pageviews|events|urls', period = '24h', unit = 'hour') { |
| 80 | + //~~ generate umami report content |
| 81 | + const generator = new ReportGenerator(umamiSite, reportContent, period, unit, umamiSiteStats, sitePageViews, siteEvents, siteMetricsUrl); |
| 82 | + const umamiOneLineReport = generator.oneLineReport(); |
| 83 | + const umamiReport = generator.detailedReport(); |
| 84 | + |
| 85 | + //~~ produce github actions results (output) |
| 86 | + await Action.produceActionResult("pageViews", umamiSiteStats.pageviews.value, null); |
| 87 | + await Action.produceActionResult("umamiOneLineReport", umamiOneLineReport); |
| 88 | + const {targetFile} = await Action.produceActionResult("umamiReport", umamiReport, outputFile); |
| 89 | + await Action.produceActionResult("umamiReportLength", umamiReport.length, null);// #14 |
| 90 | + if (isSet(targetFile)) { |
| 91 | + await Action.produceActionResult("umamiReportFile", targetFile, null); |
| 92 | + return targetFile; |
| 93 | + } |
| 94 | + return null; |
112 | 95 | } |
113 | 96 |
|
114 | | - const umami = new UmamiApiClient(server, user, password); |
115 | | - const site = isSet(domain) ? await umami.getWebsiteBy("domain", domain).catch(rethrow) : await umami.getWebsite().catch(rethrow); |
116 | | - const siteStats = await site.getStats({ period }).catch(rethrow); |
117 | | - const sitePageViews = await site.getPageviews({ period, unit, tz }).catch(rethrow); |
118 | | - const siteEvents = await site.getEvents({ period, unit, tz }).catch(rethrow); |
119 | | - const siteMetricsUrl = await site.getMetrics({ period }).catch(rethrow); |
120 | | - |
121 | | - DEBUG_ACTION && console.log(site); |
122 | | - DEBUG_ACTION && console.log(siteStats); |
123 | | - const targetFile = await Action.produceReport(site, siteStats, sitePageViews, siteEvents, siteMetricsUrl, |
124 | | - outputFile, reportContent, period); |
125 | | - if (targetFile != null) { |
126 | | - return { site, siteStats, targetFile } |
| 97 | + static async umamiReport(options) { |
| 98 | + return new Promise(async (resolve, reject) => { |
| 99 | + try { |
| 100 | + // options |
| 101 | + let {server, user, password, domain, outputFile, reportContent, period, unit, tz} = options; |
| 102 | + // default options |
| 103 | + if (!isSet(user)) { |
| 104 | + user = 'admin'; |
| 105 | + } |
| 106 | + if (!isSet(outputFile)) { |
| 107 | + outputFile = null; |
| 108 | + } |
| 109 | + if (!isSet(reportContent)) { |
| 110 | + reportContent = 'pageviews|events|urls'; |
| 111 | + } |
| 112 | + if (!isSet(period)) { |
| 113 | + period = '24h'; |
| 114 | + } |
| 115 | + if (!isSet(unit)) { |
| 116 | + unit = 'hour'; |
| 117 | + } |
| 118 | + if (!isSet(tz)) { |
| 119 | + tz = 'Europe/Paris'; |
| 120 | + } |
| 121 | + |
| 122 | + DEBUG_ACTION && console.log("options : " + JSON.stringify({ |
| 123 | + server, |
| 124 | + user, |
| 125 | + password, |
| 126 | + domain, |
| 127 | + outputFile, |
| 128 | + reportContent, |
| 129 | + period, |
| 130 | + unit, |
| 131 | + tz |
| 132 | + })); |
| 133 | + |
| 134 | + const umamiClient = new UmamiClient({server}); |
| 135 | + const authData = await umamiClient.login(user, password).catch(rethrow); |
| 136 | + |
| 137 | + const sites = await umamiClient.getSites(authData).catch(err => { |
| 138 | + console.error("errBX" + err); |
| 139 | + throw new Error(err); |
| 140 | + }) |
| 141 | + const site = umamiClient.selectSiteByDomain(sites, domain); |
| 142 | + DEBUG_ACTION && console.log(site); |
| 143 | + const siteStats = await umamiClient.getStats(authData, site, period).catch(rethrow); |
| 144 | + const sitePageViews = await umamiClient.getPageViews(authData, site, {unit, tz}, period).catch(rethrow); |
| 145 | + const siteEvents = await umamiClient.getEvents(authData, site, {unit, tz}, period).catch(rethrow); |
| 146 | + const siteMetricsUrl = await umamiClient.getMetrics(authData, site, {type: 'url'}, period).catch(rethrow); |
| 147 | + |
| 148 | + DEBUG_ACTION && console.log(siteStats); |
| 149 | + const targetFile = await Action.produceReport(site, siteStats, sitePageViews, siteEvents, siteMetricsUrl, |
| 150 | + outputFile, reportContent, period); |
| 151 | + if (isSet(targetFile)) { |
| 152 | + resolve({site, siteStats, targetFile}); |
| 153 | + } else { |
| 154 | + resolve({site, siteStats}); |
| 155 | + } |
| 156 | + } catch (err) { |
| 157 | + console.error("err" + err); |
| 158 | + reject(err); |
| 159 | + } |
| 160 | + }); |
127 | 161 | } |
128 | | - return { site, siteStats }; |
129 | | - } |
130 | 162 | } |
131 | 163 |
|
132 | 164 | export default Action; |
133 | 165 |
|
134 | 166 | const isSet = (value) => value !== null && value !== undefined && value !== ''; |
135 | 167 | const ensureDirectoryExistence = (filePath) => { |
136 | | - var dirname = path.dirname(filePath); |
137 | | - if (fs.existsSync(dirname)) { |
138 | | - return true; |
139 | | - } |
140 | | - ensureDirectoryExistence(dirname); |
141 | | - fs.mkdirSync(dirname); |
| 168 | + const dirname = path.dirname(filePath); |
| 169 | + if (fs.existsSync(dirname)) { |
| 170 | + return true; |
| 171 | + } |
| 172 | + ensureDirectoryExistence(dirname); |
| 173 | + fs.mkdirSync(dirname); |
142 | 174 | }; |
0 commit comments