Skip to content

Commit

Permalink
Fix #46 make me umami 2.9.0 compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
boly38 committed Jan 21, 2024
1 parent 494726e commit 185d1c3
Show file tree
Hide file tree
Showing 673 changed files with 126,575 additions and 2,178 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/daily_umami_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
steps:
- name: Create Daily Umami report
id: umamiReportStep
uses: boly38/action-umami-report@main
uses: boly38/action-umami-report@umami-server-2.9.0
with:
umami-server: https://${{secrets.UMAMI_SERVER}}
umami-user: ${{secrets.UMAMI_USERNAME}}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/weekly_umami_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
steps:
- name: Create Weekly Umami report
id: umamiReportStep
uses: boly38/action-umami-report@main
uses: boly38/action-umami-report@umami-server-2.9.0
with:
umami-server: https://${{secrets.UMAMI_SERVER}}
umami-user: ${{secrets.UMAMI_USERNAME}}
Expand Down
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
steps:
- name: Create Umami report
id: umamiReport
uses: boly38/action-umami-report@stable
uses: boly38/action-umami-report@umami-server-2.9.0
with:
umami-server: https://${{secrets.UMAMI_SERVER}}
umami-user: ${{secrets.UMAMI_USERNAME}}
Expand All @@ -70,10 +70,20 @@ jobs:
```
Full working sample: cf. [daily_umami_report.yml](.github/workflows/daily_umami_report.yml)
**TIP**: if your umami server version is not compatible with current GithubActions, you could change `umami-server-2.9.0` keyword by one of the [current repository tags](https://github.com/boly38/action-umami-report/tags) with `umami-server-x.y` format.

# See also

## Umami
- Umami [API](https://umami.is/docs/api) ([API client](https://github.com/jakobbouchard/umami-api-client))- [Source](https://github.com/umami-software/umami)
Umami server :
- [API](https://umami.is/docs/api)
- [source](https://github.com/umami-software/umami)

Umami API clients:
- jakobbouchard TS/JS [umami-api-client](https://github.com/jakobbouchard/umami-api-client)
- Import: `import UmamiApiClient from 'umami-api'`
- boly38 JS [umami-api-client](https://github.com/boly38/umami-api-client)
- Import: `import UmamiClient from 'umami-api-client'`

## possible next step
- send the report [by email](https://github.com/marketplace?type=actions&query=mail+), on [discord](https://github.com/marketplace?type=actions&query=discord+), etc..
Expand All @@ -91,10 +101,12 @@ cp ./env/initenv.template.sh ./env/initenv.dontpush.sh
```
* Then run manual test
```
git clone https://github.com/boly38y/action-umami-report.git
git clone https://github.com/boly38/action-umami-report.git
cd action-umami-report
npm install
npm manual.js
npm run day
npm run showResults
# check other targets in package.json
```
* you could also fork, feature branch, then submit a pull request.
Expand Down
2 changes: 1 addition & 1 deletion fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Manual from './manual.js';

const manual = new Manual();

var options = manual.getOptions()
let options = manual.getOptions()
options.period = '1month';
options.unit = 'day';
manual.fetch(options);
264 changes: 148 additions & 116 deletions lib/action.js
Original file line number Diff line number Diff line change
@@ -1,142 +1,174 @@
import path from 'path';
import fs from 'fs';
import core from '@actions/core';
import github from '@actions/github';

import axios from 'axios'
import UmamiApiClient from 'umami-api'
import UmamiClient from 'umami-api-client'

import ReportGenerator from './reportGenerator.js';
import axios from "axios";

const DEBUG_ACTION = process.env.UMAMI_DEBUG_ACTION === 'true';
const UMAMI_OUTPUT_DIRECTORY = process.env.UMAMI_OUTPUT_DIRECTORY || './umami';
const rethrow = (err) => {throw err;}
const rethrow = (err) => {
throw new Error(err);
}

class Action {

static async fetchUmamiServerApi(server, timeoutMs = 50000) {
const username = 'abc';
const password = 'abc';
console.log(`fetchUmamiServerApi`);
for (var i = 1 ; i < 10 ; i++) {
var expectedResult = [];
const action = "post " + i;
console.time(action);
const client = axios.create({ baseURL: `${server}/api`, timeout: timeoutMs });
const loginResult = await client.post("/auth/login", { username, password }).catch(error => {
console.timeEnd(action);
const message = typeof error.response !== "undefined" ? error.response.data : error.message;
const status = typeof error.response !== "undefined" ? `${error.response.status} ${error.response.statusText}`:'';
const logMessage = status !== message ? `[${status}] ${message}` : message;
if (logMessage !== '401 Unauthorized') {
console.log(`Login failed: ${logMessage}`);
} else {
expectedResult.push(error);
/**
* fetch umami api to understand flaky results
* cf. https://github.com/boly38/action-umami-report/issues/37
* @param server
* @param timeoutMs
* @returns {Promise<unknown>}
*/
static async fetchUmamiServerApi(server, timeoutMs = 50000) {
return new Promise(async (resolve, reject) => {
const username = 'admin';
const password = '075827845F';
console.log(`fetchUmamiServerApi`);
let fetchResults = [];
for (let i = 1; i < 10; i++) {
const action = "post " + i;
console.time(action);
const client = axios.create({baseURL: `${server}/api`, timeout: timeoutMs});
const loginResult = await client.post("/auth/login", {username, password}).catch(error => {
console.timeEnd(action);
const message = typeof error.response !== "undefined" ? error.response.data : error.message;
const status = typeof error.response !== "undefined" ? `${error.response.status} ${error.response.statusText}` : '';
const logMessage = status !== message ? `[${status}] ${message}` : message;
if (logMessage !== '401 Unauthorized') {
console.log(`Login failed: ${logMessage}`);
}
fetchResults.push(status);
});
if (loginResult !== undefined) {
fetchResults.push(`OK ${JSON.stringify(loginResult.status)}`);
}
}
resolve(fetchResults);
});
if (loginResult !== undefined || expectedResult.length > 0) {
return;
}
}
}

static async produceActionResult(resultName, resultValue, outputFile = null) {
const outFileAddition = outputFile !== null ? `file:${outputFile}` : '';
console.info(`produce action result (output): ${resultName} ${outFileAddition}`);
core.setOutput(resultName, resultValue);// core action output: to be declared as outputs by current job and used by another job
// disabled by #12 // core.exportVariable(resultName, resultValue);// core action env: to be used by current job by another step

var targetFile = null;
if (isSet(UMAMI_OUTPUT_DIRECTORY) && isSet(outputFile)) {
targetFile = `${UMAMI_OUTPUT_DIRECTORY}/${outputFile}`;
try {
ensureDirectoryExistence(targetFile);
fs.writeFileSync(targetFile, resultValue);
} catch (error) {
console.error(`ERROR: unable to write to ${targetFile} : ${error}`);
targetFile = null;
}
}
return { targetFile };
}


static async produceReport(umamiSite, umamiSiteStats, sitePageViews = null, siteEvents = null, siteMetricsUrl = null,
outputFile = null, reportContent = 'pageviews|events|urls', period = '24h', unit = 'hour') {
//~~ generate umami report content
const generator = new ReportGenerator(umamiSite, reportContent, period, unit, umamiSiteStats, sitePageViews, siteEvents, siteMetricsUrl);
const umamiOneLineReport = generator.oneLineReport();
const umamiReport = generator.detailedReport();

//~~ produce github actions results (output)
Action.produceActionResult("pageViews", umamiSiteStats.pageviews.value, null);
Action.produceActionResult("umamiOneLineReport", umamiOneLineReport);
const { targetFile } = await Action.produceActionResult("umamiReport", umamiReport, outputFile);
Action.produceActionResult("umamiReportLength", umamiReport.length, null);// #14
if ( isSet(targetFile) ) {
Action.produceActionResult("umamiReportFile", targetFile, null);
return targetFile;
}
return null;
}

/**
* @deprecated : please use umamiReport(...)
*/
static async umamiDailyReportV0(server, user, password, domain = '', outputFile = null, reportContent = 'pageviews|events|urls') {
var options = { server, user, password, domain, outputFile, reportContent };
return await Action.umamiReport(options);
}

static async umamiReport(options) {
// options
var { server, user, password, domain, outputFile, reportContent, period, unit, tz } = options;
// default options
if (!isSet(user)) {
user = 'admin';
}
if (!isSet(outputFile)) {
outputFile = null;
}
if (!isSet(reportContent)) {
reportContent = 'pageviews|events|urls';
}
if (!isSet(period)) {
period = '24h';
}
if (!isSet(unit)) {
unit = 'hour';

static async produceActionResult(resultName, resultValue, outputFile = null) {
const outFileAddition = outputFile !== null ? `file:${outputFile}` : '';
console.info(`produce action result (output): ${resultName} ${outFileAddition}`);
core.setOutput(resultName, resultValue);// core action output: to be declared as outputs by current job and used by another job
// disabled by #12 // core.exportVariable(resultName, resultValue);// core action env: to be used by current job by another step

let targetFile = null;
if (isSet(UMAMI_OUTPUT_DIRECTORY) && isSet(outputFile)) {
targetFile = `${UMAMI_OUTPUT_DIRECTORY}/${outputFile}`;
try {
ensureDirectoryExistence(targetFile);
fs.writeFileSync(targetFile, resultValue);
} catch (error) {
console.info(`ERROR: unable to write to ${targetFile} : ${error}`);
targetFile = null;
}
}
if (isSet(targetFile)) {
console.info(`produce action result (targetFile): ${targetFile}`);
return {targetFile};
}
return {};
}
if (!isSet(tz)) {
tz = 'Europe/Paris';


static async produceReport(umamiSite, umamiSiteStats, sitePageViews = null, siteEvents = null, siteMetricsUrl = null,
outputFile = null, reportContent = 'pageviews|events|urls', period = '24h', unit = 'hour') {
//~~ generate umami report content
const generator = new ReportGenerator(umamiSite, reportContent, period, unit, umamiSiteStats, sitePageViews, siteEvents, siteMetricsUrl);
const umamiOneLineReport = generator.oneLineReport();
const umamiReport = generator.detailedReport();

//~~ produce github actions results (output)
await Action.produceActionResult("pageViews", umamiSiteStats.pageviews.value, null);
await Action.produceActionResult("umamiOneLineReport", umamiOneLineReport);
const {targetFile} = await Action.produceActionResult("umamiReport", umamiReport, outputFile);
await Action.produceActionResult("umamiReportLength", umamiReport.length, null);// #14
if (isSet(targetFile)) {
await Action.produceActionResult("umamiReportFile", targetFile, null);
return targetFile;
}
return null;
}

const umami = new UmamiApiClient(server, user, password);
const site = isSet(domain) ? await umami.getWebsiteBy("domain", domain).catch(rethrow) : await umami.getWebsite().catch(rethrow);
const siteStats = await site.getStats({ period }).catch(rethrow);
const sitePageViews = await site.getPageviews({ period, unit, tz }).catch(rethrow);
const siteEvents = await site.getEvents({ period, unit, tz }).catch(rethrow);
const siteMetricsUrl = await site.getMetrics({ period }).catch(rethrow);

DEBUG_ACTION && console.log(site);
DEBUG_ACTION && console.log(siteStats);
const targetFile = await Action.produceReport(site, siteStats, sitePageViews, siteEvents, siteMetricsUrl,
outputFile, reportContent, period);
if (targetFile != null) {
return { site, siteStats, targetFile }
static async umamiReport(options) {
return new Promise(async (resolve, reject) => {
try {
// options
let {server, user, password, domain, outputFile, reportContent, period, unit, tz} = options;
// default options
if (!isSet(user)) {
user = 'admin';
}
if (!isSet(outputFile)) {
outputFile = null;
}
if (!isSet(reportContent)) {
reportContent = 'pageviews|events|urls';
}
if (!isSet(period)) {
period = '24h';
}
if (!isSet(unit)) {
unit = 'hour';
}
if (!isSet(tz)) {
tz = 'Europe/Paris';
}

DEBUG_ACTION && console.log("options : " + JSON.stringify({
server,
user,
password,
domain,
outputFile,
reportContent,
period,
unit,
tz
}));

const umamiClient = new UmamiClient({server});
const authData = await umamiClient.login(user, password).catch(rethrow);

const sites = await umamiClient.getSites(authData).catch(err => {
console.error("errBX" + err);
throw new Error(err);
})
const site = umamiClient.selectSiteByDomain(sites, domain);
DEBUG_ACTION && console.log(site);
const siteStats = await umamiClient.getStats(authData, site, period).catch(rethrow);
const sitePageViews = await umamiClient.getPageViews(authData, site, {unit, tz}, period).catch(rethrow);
const siteEvents = await umamiClient.getEvents(authData, site, {unit, tz}, period).catch(rethrow);
const siteMetricsUrl = await umamiClient.getMetrics(authData, site, {type: 'url'}, period).catch(rethrow);

DEBUG_ACTION && console.log(siteStats);
const targetFile = await Action.produceReport(site, siteStats, sitePageViews, siteEvents, siteMetricsUrl,
outputFile, reportContent, period);
if (isSet(targetFile)) {
resolve({site, siteStats, targetFile});
} else {
resolve({site, siteStats});
}
} catch (err) {
console.error("err" + err);
reject(err);
}
});
}
return { site, siteStats };
}
}

export default Action;

const isSet = (value) => value !== null && value !== undefined && value !== '';
const ensureDirectoryExistence = (filePath) => {
var dirname = path.dirname(filePath);
if (fs.existsSync(dirname)) {
return true;
}
ensureDirectoryExistence(dirname);
fs.mkdirSync(dirname);
const dirname = path.dirname(filePath);
if (fs.existsSync(dirname)) {
return true;
}
ensureDirectoryExistence(dirname);
fs.mkdirSync(dirname);
};
4 changes: 2 additions & 2 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const printContext = () => {
const actionUmamiReport = async function() {
try {
if (umamiServer === null || umamiServer === undefined) {
throw "please setup your environment"
throw new Error("please setup your environment");
}
var options = {};
let options = {};
options.server = umamiServer;
options.user = umamiUser;
options.password = umamiPassword;
Expand Down
Loading

0 comments on commit 185d1c3

Please sign in to comment.