Skip to content

Commit

Permalink
analytics tested
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidGOrtega committed Jul 10, 2022
1 parent d97b0c1 commit ec6b5e3
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 27 deletions.
74 changes: 47 additions & 27 deletions src/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ const fs = require('fs').promises;

const fetch = require('node-fetch');
const ProxyAgent = require('proxy-agent');
const { v4: uuidv4, v5: uuidv5 } = require('uuid');
const appdirs = require('appdirs');
const { promisify } = require('util');
const { scrypt } = require('crypto');
const { v4: uuidv4, v5: uuidv5, parse } = require('uuid');
const { userConfigDir } = require('appdirs');
const winston = require('winston');

const { version: VERSION } = require('../package.json');
const { exec } = require('./utils');
const { exec, fileExists } = require('./utils');

const {
TPI_ANALYTICS_ENDPOINT = 'https://telemetry.cml.dev/api/v1/s2s/event?ip_policy=strict',
Expand All @@ -32,8 +34,16 @@ const {

const ID_DO_NOT_TRACK = 'do-not-track';

const deterministic = (str) => {
return uuidv5(str, 'iterative.ai');
const deterministic = async (data) => {
const namespace = uuidv5('iterative.ai', uuidv5.DNS);
const name = await promisify(scrypt)(data, parse(namespace), 8, {
N: 1 << 16,
r: 8,
p: 1,
maxmem: 128 * 1024 ** 2
});

return uuidv5(name.toString('hex'), namespace);
};

const guessCI = () => {
Expand All @@ -50,7 +60,7 @@ const isCI = () => {
return ci > 0;
};

const groupId = () => {
const groupId = async () => {
if (!isCI()) return '';

const ci = guessCI();
Expand All @@ -63,7 +73,7 @@ const groupId = () => {
rawId = BITBUCKET_WORKSPACE;
}

return deterministic(rawId);
return await deterministic(rawId);
};

const userId = async () => {
Expand All @@ -83,26 +93,27 @@ const userId = async () => {
rawId = await exec(`git log -1 --pretty=format:'%ae'`);
}

return deterministic(rawId);
return await deterministic(rawId);
}

let id = uuidv4();
const oldPath = appdirs.userConfigDir('dvc/user_id', 'iterative');
const newPath = appdirs.userConfigDir('iterative/telemetry');
if (path.exists(newPath)) {
id = await fs.readFile(newPath);
const oldPath = userConfigDir('dvc/user_id', 'iterative');
const newPath = userConfigDir('iterative/telemetry');

if (await fileExists(newPath)) {
id = (await fs.readFile(newPath)).toString('utf-8');
} else {
if (path.exists(oldPath)) {
const json = await fs.readFile(oldPath);
({ id } = JSON.parse(json));
if (await fileExists(oldPath)) {
const json = (await fs.readFile(oldPath)).toString('utf-8');
({ user_id: id } = JSON.parse(json));
}

await fs.mkdir(path.diname(newPath), { recursive: true });
await fs.mkdir(path.dirname(newPath), { recursive: true });
await fs.writeFile(newPath, id);
}

if (!path.exists(oldPath) && id !== ID_DO_NOT_TRACK) {
await fs.mkdir(path.diname(oldPath), { recursive: true });
if (!(await fileExists(oldPath)) && id !== ID_DO_NOT_TRACK) {
await fs.mkdir(path.dirname(oldPath), { recursive: true });
await fs.writeFile(oldPath, JSON.stringify({ user_id: id }));
}

Expand All @@ -116,13 +127,18 @@ const OS = () => {
return process.platform;
};

const jitsuEventPayload = async ({ action, extra = {}, error = '' } = {}) => {
extra.ci = guessCI();
const { backend, ...extraRest } = extra;
const jitsuEventPayload = async ({
action = '',
error = '',
extra = {}
} = {}) => {
const { cloud: backend = '', ...extraRest } = extra;
extraRest.ci = guessCI();

console.log(extraRest);
return {
user_id: userId(),
groupId: groupId(),
user_id: await userId(),
group_id: await groupId(),
action,
interface: 'cli',
tool_name: 'cml',
Expand All @@ -132,27 +148,31 @@ const jitsuEventPayload = async ({ action, extra = {}, error = '' } = {}) => {
os_version: os.release(),
backend,
error,
extra: { ...extraRest }
extra: extraRest
};
};

const send = async () => {
const send = async ({
endpoint = TPI_ANALYTICS_ENDPOINT,
token = TPI_ANALYTICS_TOKEN
} = {}) => {
try {
if (ITERATIVE_DO_NOT_TRACK) return;

const payload = await jitsuEventPayload();
if (payload.id === ID_DO_NOT_TRACK) return;

await fetch(TPI_ANALYTICS_ENDPOINT, {
await fetch(endpoint, {
method: 'POST',
headers: {
'X-Auth-Toke': TPI_ANALYTICS_TOKEN,
'X-Auth-Toke': token,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload),
agent: new ProxyAgent()
});
} catch (err) {
console.log(`Send analytics failed: ${err.message}`);
winston.debug(`Send analytics failed: ${err.message}`);
}
};
Expand Down
43 changes: 43 additions & 0 deletions src/analytics.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { send, jitsuEventPayload, isCI } = require('./analytics');

describe('analytics tests', () => {
test('userId', async () => {
const action = 'test';
const cloud = 'azure';
const more = { one: 1, two: 2 };
const extra = { cloud, ...more };
const error = 'Ouch!';
const regex = /\d+\.\d+\.\d+/;

const pl = await jitsuEventPayload({ action, error, extra });
expect(pl.user_id.length).toBe(36);
expect(pl.action).toBe(action);
expect(pl.interface).toBe('cli');
expect(pl.tool_name).toBe('cml');
expect(regex.test(pl.tool_version)).toBe(true);
expect(pl.tool_source).toBe('');
expect(pl.os_name.length > 0).toBe(true);
expect(pl.os_version.length > 0).toBe(true);
expect(pl.backend).toBe(cloud);
expect(pl.error).toBe(error);
expect(Object.keys(pl.extra).sort()).toEqual(
['ci'].concat(Object.keys(more)).sort()
);

if (isCI()) {
expect(pl.group_id.length).toBe(36);
expect(pl.extra.ci.length > 0).toBe(true);
}
});

test('Send should never fail', async () => {
let error = null;
try {
await send({ endpoint: 'https://notfound.cml.dev' });
} catch (err) {
error = err;
}

expect(error).toBeNull();
});
});
7 changes: 7 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ const tfCapture = async (command, args = [], options = {}) => {
});
};

const fileExists = (path) =>
fs.promises.stat(path).then(
() => true,
() => false
);

exports.tfCapture = tfCapture;
exports.waitForever = waitForever;
exports.exec = exec;
Expand All @@ -228,3 +234,4 @@ exports.watermarkUri = watermarkUri;
exports.download = download;
exports.sshConnection = sshConnection;
exports.gpuPresent = gpuPresent;
exports.fileExists = fileExists;

1 comment on commit ec6b5e3

@github-actions

This comment was marked as off-topic.

Please sign in to comment.