@nomiclabs/buidler
Version:
Buidler is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
185 lines • 7.59 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const abort_controller_1 = __importDefault(require("abort-controller"));
const debug_1 = __importDefault(require("debug"));
const ethereumjs_util_1 = require("ethereumjs-util");
const node_fetch_1 = __importDefault(require("node-fetch"));
const os_1 = __importDefault(require("os"));
const qs_1 = __importDefault(require("qs"));
const v4_1 = __importDefault(require("uuid/v4"));
const builtinTaskNames = __importStar(require("../../builtin-tasks/task-names"));
const execution_mode_1 = require("../core/execution-mode");
const ci_detection_1 = require("../util/ci-detection");
const global_dir_1 = require("../util/global-dir");
const packageInfo_1 = require("../util/packageInfo");
const log = debug_1.default("buidler:core:analytics");
const googleAnalyticsUrl = "https://www.google-analytics.com/collect";
class Analytics {
constructor({ projectId, clientId, enabled, userType, }) {
// Buidler's tracking id. I guess there's no other choice than keeping it here.
this._trackingId = "UA-117668706-3";
this._projectId = projectId;
this._clientId = clientId;
this._enabled = enabled && !execution_mode_1.isLocalDev() && !ci_detection_1.isRunningOnCiServer();
this._userType = userType;
}
static async getInstance(rootPath, enabled) {
const analytics = new Analytics({
projectId: getProjectId(rootPath),
clientId: await getClientId(),
enabled,
userType: getUserType(),
});
return analytics;
}
/**
* Attempt to send a hit to Google Analytics using the Measurement Protocol.
* This function returns immediately after starting the request, returning a function for aborting it.
* The idea is that we don't want Buidler tasks to be slowed down by a slow network request, so
* Buidler can abort the request if it takes too much time.
*
* Trying to abort a successfully completed request is a no-op, so it's always safe to call it.
*
* @param taskName The name of the task to be logged
*
* @returns The abort function
*/
async sendTaskHit(taskName) {
if (this._isABuiltinTaskName(taskName)) {
taskName = "builtin";
}
else {
taskName = "custom";
}
if (!this._enabled) {
return [() => { }, Promise.resolve()];
}
return this._sendHit(await this._taskHit(taskName));
}
_isABuiltinTaskName(taskName) {
return Object.values(builtinTaskNames).includes(taskName);
}
async _taskHit(taskName) {
return {
// Measurement protocol version.
v: "1",
// Hit type, we're only using pageviews for now.
t: "pageview",
// Buidler's tracking Id.
tid: this._trackingId,
// Client Id.
cid: this._clientId,
// Document path, must start with a '/'.
dp: `/task/${taskName}`,
// Host name.
dh: "cli.buidler.dev",
// User agent, must be present.
// We use it to inform Node version used and OS.
// Example:
// Node/v8.12.0 (Darwin 17.7.0)
ua: getUserAgent(),
// We're using the following values (Campaign source, Campaign medium) to track
// whether the user is a Developer or CI, as Custom Dimensions are not working for us atm.
cs: this._userType,
cm: "User Type",
// We're using custom dimensions for tracking different user projects, and user types (Developer/CI).
//
// See the following link for docs on these paremeters:
// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#pr_cd_
//
// See the following link for setting up our custom dimensions in the Google Analytics dashboard
// https://support.google.com/tagmanager/answer/6164990
//
// Custom dimension 1: Project Id
// This is computed as the keccak256 hash of the project's absolute path.
cd1: this._projectId,
// Custom dimension 2: User type
// Possible values: "CI", "Developer".
cd2: this._userType,
// Custom dimension 3: Buidler Version
// Example: "Buidler 1.0.0".
cd3: await getBuidlerVersion(),
};
}
_sendHit(hit) {
log(`Sending hit for ${hit.dp}`);
const controller = new abort_controller_1.default();
const abortAnalytics = () => {
log(`Aborting hit for ${JSON.stringify(hit.dp)}`);
controller.abort();
};
const hitPayload = qs_1.default.stringify(hit);
log(`Hit payload: ${JSON.stringify(hit)}`);
const hitPromise = node_fetch_1.default(googleAnalyticsUrl, {
body: hitPayload,
method: "POST",
signal: controller.signal,
})
.then(() => {
log(`Hit for ${JSON.stringify(hit.dp)} sent successfully`);
})
// We're not really interested in handling failed analytics requests
.catch(() => {
log("Hit request failed");
});
return [abortAnalytics, hitPromise];
}
}
exports.Analytics = Analytics;
async function getClientId() {
let clientId = await global_dir_1.readAnalyticsId();
if (clientId === null) {
clientId = await global_dir_1.readLegacyAnalyticsId();
if (clientId === null) {
log("Client Id not found, generating a new one");
clientId = v4_1.default();
}
await global_dir_1.writeAnalyticsId(clientId);
}
return clientId;
}
function getProjectId(rootPath) {
log(`Computing Project Id for ${rootPath}`);
const projectId = ethereumjs_util_1.keccak256(rootPath).toString("hex");
log(`Project Id set to ${projectId}`);
return projectId;
}
function getUserType() {
return ci_detection_1.isRunningOnCiServer() ? "CI" : "Developer";
}
/**
* At the moment, we couldn't find a reliably way to report the OS () in Node,
* as the versions reported by the various `os` APIs (`os.platform()`, `os.type()`, etc)
* return values different to those expected by Google Analytics
* We decided to take the compromise of just reporting the OS Platform (OSX/Linux/Windows) for now (version information is bogus for now).
*/
function getOperatingSystem() {
switch (os_1.default.type()) {
case "Windows_NT":
return "(Windows NT 6.1; Win64; x64)";
case "Darwin":
return "(Macintosh; Intel Mac OS X 10_13_6)";
case "Linux":
return "(X11; Linux x86_64)";
default:
return "(Unknown)";
}
}
function getUserAgent() {
return `Node/${process.version} ${getOperatingSystem()}`;
}
async function getBuidlerVersion() {
const { version } = await packageInfo_1.getPackageJson();
return `Buidler ${version}`;
}
//# sourceMappingURL=analytics.js.map
;