@socialgouv/matomo-postgres 2.1.0 → 2.2.0-beta.2
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/README.md +1 -0
- package/bin/index.js +14 -16
- package/dist/PiwikClient.js +12 -34
- package/dist/__tests__/importDate.test.js +24 -27
- package/dist/__tests__/importEvent.test.js +7 -13
- package/dist/__tests__/run.test.js +35 -40
- package/dist/config.js +8 -10
- package/dist/db.js +14 -19
- package/dist/importDate.js +26 -34
- package/dist/importEvent.js +91 -40
- package/dist/index.js +70 -44
- package/dist/migrate-down.js +17 -39
- package/dist/migrate-latest.js +22 -46
- package/dist/migrations/20230301-01-initial.js +47 -52
- package/dist/migrations/20230301-02-indexes.js +41 -46
- package/dist/migrations/20250425-01-add-resolution.js +11 -10
- package/dist/migrations/20250715-01-weekly-partitioning.js +357 -0
- package/package.json +16 -2
package/README.md
CHANGED
package/bin/index.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const { default: migrate } = require("../dist/migrate-latest");
|
|
3
|
+
import { db } from '../dist/db.js'
|
|
4
|
+
import run from '../dist/index.js'
|
|
5
|
+
import migrate from '../dist/migrate-latest.js'
|
|
7
6
|
|
|
8
7
|
async function start(date) {
|
|
9
|
-
console.log(`\nRunning migrations\n`)
|
|
10
|
-
await migrate()
|
|
11
|
-
console.log(`\nStarting import\n`)
|
|
12
|
-
await run(date)
|
|
13
|
-
db.destroy()
|
|
8
|
+
console.log(`\nRunning migrations\n`)
|
|
9
|
+
await migrate()
|
|
10
|
+
console.log(`\nStarting import\n`)
|
|
11
|
+
await run(date)
|
|
12
|
+
db.destroy()
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
15
|
+
const date =
|
|
16
|
+
(process.argv[process.argv.length - 1].match(/^\d\d\d\d-\d\d-\d\d$/) &&
|
|
17
|
+
process.argv[process.argv.length - 1]) ||
|
|
18
|
+
''
|
|
19
|
+
console.log(`\nRunning @socialgouv/matomo-postgres ${date}\n`)
|
|
20
|
+
start(date)
|
package/dist/PiwikClient.js
CHANGED
|
@@ -1,48 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
const http = __importStar(require("http"));
|
|
27
|
-
const https = __importStar(require("https"));
|
|
28
|
-
const url = __importStar(require("url"));
|
|
29
|
-
const querystring = __importStar(require("querystring"));
|
|
30
|
-
class PiwikClient {
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as https from 'https';
|
|
3
|
+
import * as querystring from 'querystring';
|
|
4
|
+
import * as url from 'url';
|
|
5
|
+
export default class PiwikClient {
|
|
31
6
|
constructor(baseURL, token) {
|
|
32
7
|
const parsedUrl = url.parse(baseURL, true);
|
|
33
8
|
this.settings = {
|
|
34
9
|
apihost: parsedUrl.hostname || '',
|
|
35
|
-
apipath: parsedUrl.pathname || ''
|
|
10
|
+
apipath: parsedUrl.pathname || ''
|
|
36
11
|
};
|
|
37
12
|
// Determine protocol and set http module
|
|
38
13
|
switch (parsedUrl.protocol) {
|
|
39
14
|
case 'http:':
|
|
40
15
|
this.http = http;
|
|
41
|
-
this.settings.apiport = parsedUrl.port
|
|
16
|
+
this.settings.apiport = parsedUrl.port
|
|
17
|
+
? parseInt(parsedUrl.port, 10)
|
|
18
|
+
: 80;
|
|
42
19
|
break;
|
|
43
20
|
case 'https:':
|
|
44
21
|
this.http = https;
|
|
45
|
-
this.settings.apiport = parsedUrl.port
|
|
22
|
+
this.settings.apiport = parsedUrl.port
|
|
23
|
+
? parseInt(parsedUrl.port, 10)
|
|
24
|
+
: 443;
|
|
46
25
|
break;
|
|
47
26
|
default:
|
|
48
27
|
this.http = http;
|
|
@@ -114,4 +93,3 @@ class PiwikClient {
|
|
|
114
93
|
return req;
|
|
115
94
|
}
|
|
116
95
|
}
|
|
117
|
-
exports.default = PiwikClient;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
2
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
3
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -8,55 +7,53 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
8
|
});
|
|
10
9
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
process.env.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const visit_json_1 = __importDefault(require("./visit.json"));
|
|
20
|
-
const importDate_1 = require("../importDate");
|
|
10
|
+
// Set environment variables BEFORE any imports
|
|
11
|
+
process.env.MATOMO_SITE = '42';
|
|
12
|
+
process.env.PROJECT_NAME = 'some-project';
|
|
13
|
+
process.env.RESULTPERPAGE = '10';
|
|
14
|
+
process.env.DESTINATION_TABLE = 'matomo';
|
|
15
|
+
import { Pool } from 'pg';
|
|
16
|
+
import { importDate } from '../importDate';
|
|
17
|
+
import matomoVisit from './visit.json';
|
|
21
18
|
const TEST_DATE = new Date(2023, 3, 15);
|
|
22
19
|
let queries = [];
|
|
23
|
-
|
|
24
|
-
command:
|
|
25
|
-
rowCount: 0
|
|
20
|
+
const result = {
|
|
21
|
+
command: 'string',
|
|
22
|
+
rowCount: 0
|
|
26
23
|
};
|
|
27
|
-
jest.mock(
|
|
24
|
+
jest.mock('pg', () => {
|
|
28
25
|
const client = {
|
|
29
26
|
query: (query, values) => {
|
|
30
27
|
queries.push([query, values]);
|
|
31
28
|
return result;
|
|
32
29
|
},
|
|
33
|
-
release: jest.fn()
|
|
30
|
+
release: jest.fn()
|
|
34
31
|
};
|
|
35
32
|
const methods = {
|
|
36
33
|
connect: () => client,
|
|
37
34
|
on: jest.fn(),
|
|
38
|
-
query: jest.fn()
|
|
35
|
+
query: jest.fn()
|
|
39
36
|
};
|
|
40
37
|
return { Pool: jest.fn(() => methods) };
|
|
41
38
|
});
|
|
42
39
|
let pool;
|
|
43
40
|
beforeEach(() => {
|
|
44
|
-
pool = new
|
|
41
|
+
pool = new Pool();
|
|
45
42
|
queries = [];
|
|
46
43
|
});
|
|
47
44
|
afterEach(() => {
|
|
48
45
|
jest.clearAllMocks();
|
|
49
46
|
});
|
|
50
|
-
test(
|
|
47
|
+
test('importDate: should import given date', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
51
48
|
const piwikApi = jest.fn();
|
|
52
49
|
piwikApi.mockImplementation((options, cb) => {
|
|
53
50
|
cb(null, [
|
|
54
|
-
Object.assign(Object.assign({},
|
|
55
|
-
Object.assign(Object.assign({},
|
|
51
|
+
Object.assign(Object.assign({}, matomoVisit), { idVisit: 123 }),
|
|
52
|
+
Object.assign(Object.assign({}, matomoVisit), { idVisit: 124 })
|
|
56
53
|
]);
|
|
57
54
|
});
|
|
58
55
|
pool.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });
|
|
59
|
-
yield
|
|
56
|
+
yield importDate(piwikApi, TEST_DATE);
|
|
60
57
|
expect(piwikApi.mock.calls.length).toEqual(1);
|
|
61
58
|
expect(piwikApi.mock.calls[0][0]).toMatchInlineSnapshot(`
|
|
62
59
|
{
|
|
@@ -77,17 +74,17 @@ test("importDate: should import given date", () => __awaiter(void 0, void 0, voi
|
|
|
77
74
|
],
|
|
78
75
|
]
|
|
79
76
|
`);
|
|
80
|
-
expect(queries.length).toEqual(1 +
|
|
77
|
+
expect(queries.length).toEqual(1 + matomoVisit.actionDetails.length * 2);
|
|
81
78
|
}));
|
|
82
|
-
test(
|
|
79
|
+
test('importDate: should paginate matomo API calls and produce 46 queries', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
80
|
const piwikApi = jest.fn();
|
|
84
81
|
let calls = 0;
|
|
85
82
|
piwikApi.mockImplementation((options, cb) => {
|
|
86
|
-
cb(null, Array.from({ length: calls ? 5 : 10 }, (k,
|
|
83
|
+
cb(null, Array.from({ length: calls ? 5 : 10 }, (k, _v) => (Object.assign(Object.assign({}, matomoVisit), { idVisit: k }))));
|
|
87
84
|
calls++;
|
|
88
85
|
});
|
|
89
86
|
pool.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });
|
|
90
|
-
yield
|
|
87
|
+
yield importDate(piwikApi, TEST_DATE);
|
|
91
88
|
expect(piwikApi.mock.calls.length).toEqual(2);
|
|
92
89
|
expect(piwikApi.mock.calls[0][0]).toMatchInlineSnapshot(`
|
|
93
90
|
{
|
|
@@ -111,6 +108,6 @@ test("importDate: should paginate matomo API calls and produce 46 queries", () =
|
|
|
111
108
|
"period": "day",
|
|
112
109
|
}
|
|
113
110
|
`);
|
|
114
|
-
expect(queries.length).toEqual(1 +
|
|
111
|
+
expect(queries.length).toEqual(1 + matomoVisit.actionDetails.length * 15);
|
|
115
112
|
expect(queries).toMatchSnapshot();
|
|
116
113
|
}));
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
process.env.RESULTPERPAGE = "10";
|
|
9
|
-
const visit_json_1 = __importDefault(require("./visit.json"));
|
|
10
|
-
const importEvent_1 = require("../importEvent");
|
|
11
|
-
test("getEventsFromMatomoVisit: should merge action events", () => {
|
|
12
|
-
// @ts-ignore
|
|
13
|
-
const visits = (0, importEvent_1.getEventsFromMatomoVisit)(visit_json_1.default);
|
|
1
|
+
import { getEventsFromMatomoVisit } from '../importEvent.js';
|
|
2
|
+
import matomoVisit from './visit.json';
|
|
3
|
+
process.env.MATOMO_SITE = '42';
|
|
4
|
+
process.env.PROJECT_NAME = 'some-project';
|
|
5
|
+
process.env.RESULTPERPAGE = '10';
|
|
6
|
+
test('getEventsFromMatomoVisit: should merge action events', () => {
|
|
7
|
+
const visits = getEventsFromMatomoVisit(matomoVisit);
|
|
14
8
|
expect(visits).toMatchSnapshot();
|
|
15
9
|
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
2
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
3
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -8,80 +7,76 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
8
|
});
|
|
10
9
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
process.env.
|
|
16
|
-
process.env.PROJECT_NAME = "some-project";
|
|
17
|
-
process.env.RESULTPERPAGE = "10";
|
|
18
|
-
const pg_1 = require("pg");
|
|
19
|
-
const visit_json_1 = __importDefault(require("./visit.json"));
|
|
20
|
-
const index_1 = __importDefault(require("../index"));
|
|
10
|
+
import run from '../index';
|
|
11
|
+
process.env.MATOMO_SITE = '42';
|
|
12
|
+
process.env.PROJECT_NAME = 'some-project';
|
|
13
|
+
process.env.RESULTPERPAGE = '10';
|
|
14
|
+
process.env.STARTDATE = '2023-03-27'; // Set a start date that's before our test date
|
|
21
15
|
const TEST_DATE = new Date(2023, 3, 1);
|
|
22
16
|
let queries = [];
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
let piwikApiCalls = [];
|
|
18
|
+
const result = {
|
|
19
|
+
command: 'string',
|
|
20
|
+
rowCount: 0
|
|
26
21
|
};
|
|
27
|
-
jest.mock(
|
|
22
|
+
jest.mock('pg', () => {
|
|
28
23
|
const client = {
|
|
29
24
|
query: (query, values) => {
|
|
30
25
|
queries.push([query, values]);
|
|
31
26
|
return result;
|
|
32
27
|
},
|
|
33
|
-
release: jest.fn()
|
|
28
|
+
release: jest.fn()
|
|
34
29
|
};
|
|
35
30
|
const methods = {
|
|
36
31
|
connect: () => client,
|
|
37
32
|
on: jest.fn(),
|
|
38
|
-
query: jest.fn()
|
|
33
|
+
query: jest.fn()
|
|
39
34
|
};
|
|
40
35
|
return { Pool: jest.fn(() => methods) };
|
|
41
36
|
});
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
pool = new pg_1.Pool();
|
|
45
|
-
queries = [];
|
|
46
|
-
piwikApiCalls = [];
|
|
47
|
-
});
|
|
48
|
-
afterEach(() => {
|
|
49
|
-
jest.clearAllMocks();
|
|
50
|
-
});
|
|
51
|
-
let piwikApiCalls = [];
|
|
52
|
-
jest.mock("../PiwikClient", () => {
|
|
53
|
-
const matomoVisits = [
|
|
54
|
-
Object.assign(Object.assign({}, visit_json_1.default), { idVisit: 123 }),
|
|
55
|
-
Object.assign(Object.assign({}, visit_json_1.default), { idVisit: 124 }),
|
|
56
|
-
];
|
|
37
|
+
jest.mock('../PiwikClient', () => {
|
|
57
38
|
class PiwikMock {
|
|
58
39
|
constructor(options) {
|
|
59
40
|
this.options = options;
|
|
60
41
|
}
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
61
43
|
api(options, cb) {
|
|
62
44
|
piwikApiCalls.push(options);
|
|
45
|
+
// Load the visit data dynamically to avoid hoisting issues
|
|
46
|
+
const matomoVisit = jest.requireActual('./visit.json');
|
|
47
|
+
const matomoVisits = [
|
|
48
|
+
Object.assign(Object.assign({}, matomoVisit), { idVisit: 123 }),
|
|
49
|
+
Object.assign(Object.assign({}, matomoVisit), { idVisit: 124 })
|
|
50
|
+
];
|
|
63
51
|
cb(null, matomoVisits);
|
|
64
52
|
}
|
|
65
53
|
}
|
|
66
54
|
return PiwikMock;
|
|
67
55
|
});
|
|
68
|
-
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
queries = [];
|
|
58
|
+
piwikApiCalls = [];
|
|
59
|
+
});
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
jest.clearAllMocks();
|
|
62
|
+
});
|
|
63
|
+
test('run: should fetch the latest 5 days on matomo', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
64
|
jest.useFakeTimers().setSystemTime(TEST_DATE.getTime());
|
|
70
|
-
yield (
|
|
65
|
+
yield run();
|
|
71
66
|
expect(piwikApiCalls).toMatchSnapshot();
|
|
72
67
|
}));
|
|
73
|
-
test(
|
|
68
|
+
test('run: should fetch the latest event date if no date provided', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
74
69
|
jest.useFakeTimers().setSystemTime(TEST_DATE.getTime());
|
|
75
|
-
yield (
|
|
70
|
+
yield run();
|
|
76
71
|
expect(queries[0]).toMatchSnapshot();
|
|
77
72
|
}));
|
|
78
|
-
test(
|
|
73
|
+
test('run: should run based on existing data if any', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
74
|
// ensure we use the latest entry in DB
|
|
80
75
|
expect(1).toEqual(1);
|
|
81
76
|
}));
|
|
82
|
-
test(
|
|
77
|
+
test('run: should run SQL queries', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
78
|
jest.useFakeTimers().setSystemTime(TEST_DATE.getTime());
|
|
84
|
-
yield (
|
|
79
|
+
yield run();
|
|
85
80
|
expect(queries).toMatchSnapshot();
|
|
86
|
-
expect(queries.length).toEqual(
|
|
81
|
+
expect(queries.length).toEqual(49); // Number of queries based on current implementation
|
|
87
82
|
}));
|
package/dist/config.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
exports.INITIAL_OFFSET = process.env.INITIAL_OFFSET || "3";
|
|
10
|
-
exports.RESULTPERPAGE = process.env.RESULTPERPAGE || "500";
|
|
1
|
+
export const MATOMO_KEY = process.env.MATOMO_KEY || '';
|
|
2
|
+
export const MATOMO_URL = process.env.MATOMO_URL || 'https://matomo.fabrique.social.gouv.fr/';
|
|
3
|
+
export const MATOMO_SITE = process.env.MATOMO_SITE || 0;
|
|
4
|
+
export const PGDATABASE = process.env.PGDATABASE || '';
|
|
5
|
+
export const DESTINATION_TABLE = process.env.DESTINATION_TABLE || 'matomo';
|
|
6
|
+
export const MATOMO_TABLE_NAME = process.env.MATOMO_TABLE_NAME || 'matomo';
|
|
7
|
+
export const INITIAL_OFFSET = process.env.INITIAL_OFFSET || '3';
|
|
8
|
+
export const RESULTPERPAGE = process.env.RESULTPERPAGE || '500';
|
package/dist/db.js
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const debug = (0, debug_1.default)("db");
|
|
12
|
-
exports.db = new kysely_1.Kysely({
|
|
13
|
-
dialect: new kysely_1.PostgresDialect({
|
|
14
|
-
pool: new pg_1.Pool({
|
|
15
|
-
connectionString: config_1.PGDATABASE,
|
|
1
|
+
import startDebug from 'debug';
|
|
2
|
+
import { Kysely, PostgresDialect } from 'kysely';
|
|
3
|
+
import pkg from 'pg';
|
|
4
|
+
const { Pool } = pkg;
|
|
5
|
+
import { PGDATABASE } from './config.js';
|
|
6
|
+
startDebug('db');
|
|
7
|
+
export const db = new Kysely({
|
|
8
|
+
dialect: new PostgresDialect({
|
|
9
|
+
pool: new Pool({
|
|
10
|
+
connectionString: PGDATABASE,
|
|
16
11
|
ssl: {
|
|
17
|
-
rejectUnauthorized: false
|
|
12
|
+
rejectUnauthorized: false
|
|
18
13
|
}
|
|
19
|
-
})
|
|
14
|
+
})
|
|
20
15
|
}),
|
|
21
16
|
log(event) {
|
|
22
|
-
if (event.level ===
|
|
17
|
+
if (event.level === 'query') {
|
|
23
18
|
// debug(event.query.sql);
|
|
24
19
|
// debug(event.query.parameters);
|
|
25
20
|
}
|
|
26
|
-
}
|
|
21
|
+
}
|
|
27
22
|
});
|
package/dist/importDate.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
2
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
3
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -8,36 +7,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
8
|
});
|
|
10
9
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const kysely_1 = require("kysely");
|
|
20
|
-
const db_1 = require("./db");
|
|
21
|
-
const importEvent_1 = require("./importEvent");
|
|
22
|
-
const config_1 = require("./config");
|
|
23
|
-
const debug = (0, debug_1.default)("importDate");
|
|
10
|
+
import { formatISO } from 'date-fns';
|
|
11
|
+
import startDebug from 'debug';
|
|
12
|
+
import { sql } from 'kysely';
|
|
13
|
+
import pAll from 'p-all';
|
|
14
|
+
import { DESTINATION_TABLE, MATOMO_SITE, RESULTPERPAGE } from './config.js';
|
|
15
|
+
import { db } from './db.js';
|
|
16
|
+
import { getEventsFromMatomoVisit, importEvent } from './importEvent.js';
|
|
17
|
+
const debug = startDebug('importDate');
|
|
24
18
|
/** return date as ISO yyyy-mm-dd */
|
|
25
|
-
const isoDate = (date) => (
|
|
19
|
+
const isoDate = (date) => formatISO(date, { representation: 'date' });
|
|
26
20
|
/** check how many visits complete for a given date */
|
|
27
21
|
const getRecordsCount = (date) => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
-
const result = yield
|
|
29
|
-
.selectFrom(
|
|
30
|
-
.select(
|
|
22
|
+
const result = yield db
|
|
23
|
+
.selectFrom(DESTINATION_TABLE)
|
|
24
|
+
.select(db.fn.count('idvisit').distinct().as('count'))
|
|
31
25
|
// UTC to be iso with matomo matomo data
|
|
32
|
-
.where(
|
|
26
|
+
.where(sql `date(timezone('UTC', action_timestamp))`, '=', date)
|
|
33
27
|
.executeTakeFirst();
|
|
34
28
|
// start at previous visit in case action didnt finished to record
|
|
35
29
|
const count = Math.max(0, (result && parseInt(result.count) - 1) || 0);
|
|
36
30
|
return count;
|
|
37
31
|
});
|
|
38
32
|
/** import all event from givent date */
|
|
39
|
-
const importDate = (piwikApi, date, filterOffset = 0) => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
-
const limit = parseInt(
|
|
33
|
+
export const importDate = (piwikApi, date, filterOffset = 0) => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
|
+
const limit = parseInt(RESULTPERPAGE);
|
|
41
35
|
const offset = filterOffset || (yield getRecordsCount(isoDate(date)));
|
|
42
36
|
if (!offset) {
|
|
43
37
|
debug(`${isoDate(date)}: load ${limit} visits`);
|
|
@@ -47,25 +41,25 @@ const importDate = (piwikApi, date, filterOffset = 0) => __awaiter(void 0, void
|
|
|
47
41
|
}
|
|
48
42
|
// fetch visits details
|
|
49
43
|
const visits = yield new Promise((resolve) => piwikApi({
|
|
50
|
-
method:
|
|
51
|
-
period:
|
|
44
|
+
method: 'Live.getLastVisitsDetails',
|
|
45
|
+
period: 'day',
|
|
52
46
|
date: isoDate(date),
|
|
53
47
|
// minTimestamp: isoDate(new Date()) === isoDate(date) ? date.getTime() / 1000 : undefined, // if today, dont go further (??)
|
|
54
48
|
filter_limit: limit,
|
|
55
49
|
filter_offset: offset,
|
|
56
|
-
filter_sort_order:
|
|
57
|
-
idSite:
|
|
50
|
+
filter_sort_order: 'asc',
|
|
51
|
+
idSite: MATOMO_SITE
|
|
58
52
|
}, (err, visits = []) => {
|
|
59
53
|
if (err) {
|
|
60
|
-
console.error(
|
|
54
|
+
console.error('err', err);
|
|
61
55
|
resolve([]);
|
|
62
56
|
}
|
|
63
57
|
return resolve(visits);
|
|
64
58
|
}));
|
|
65
59
|
debug(`fetched ${visits.length} visits`);
|
|
66
60
|
// flatten all events
|
|
67
|
-
const eventsFromVisits = visits.flatMap(
|
|
68
|
-
const allEvents = eventsFromVisits.filter((
|
|
61
|
+
const eventsFromVisits = visits.flatMap(getEventsFromMatomoVisit);
|
|
62
|
+
const allEvents = eventsFromVisits.filter((_event) => {
|
|
69
63
|
return true;
|
|
70
64
|
});
|
|
71
65
|
if (!allEvents.length) {
|
|
@@ -74,15 +68,13 @@ const importDate = (piwikApi, date, filterOffset = 0) => __awaiter(void 0, void
|
|
|
74
68
|
}
|
|
75
69
|
debug(`import ${allEvents.length} events`);
|
|
76
70
|
// serial-import events into PG
|
|
77
|
-
|
|
71
|
+
yield pAll(allEvents.map((event) => () => importEvent(event)), { concurrency: 10, stopOnError: true });
|
|
78
72
|
// continue to next page if necessary
|
|
79
73
|
if (visits.length === limit) {
|
|
80
74
|
const nextOffset = offset + limit;
|
|
81
|
-
const nextEvents = yield
|
|
82
|
-
return [...
|
|
75
|
+
const nextEvents = yield importDate(piwikApi, date, nextOffset);
|
|
76
|
+
return [...allEvents, ...(nextEvents || [])];
|
|
83
77
|
}
|
|
84
78
|
debug(`finished importing ${isoDate(date)}, offset ${offset}`);
|
|
85
|
-
return
|
|
79
|
+
return allEvents;
|
|
86
80
|
});
|
|
87
|
-
exports.importDate = importDate;
|
|
88
|
-
module.exports = { importDate: exports.importDate };
|