Skip to content

Commit

Permalink
Merge pull request #613 from near/mixpanel/identity-management
Browse files Browse the repository at this point in the history
Mixpanel/identity management
  • Loading branch information
icerove authored Jan 23, 2021
2 parents 5e5caae + e7d4104 commit 9f50e49
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 10 deletions.
1 change: 1 addition & 0 deletions commands/add-key.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const connect = require('../utils/connect');
const inspectResponse = require('../utils/inspect-response');
const { utils } = require('near-api-js');


module.exports = {
command: 'add-key <account-id> <access-key>',
desc: 'Add an access key to given account',
Expand Down
2 changes: 2 additions & 0 deletions commands/create-account.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { KeyPair } = require('near-api-js');
const inspectResponse = require('../utils/inspect-response');
// Top-level account (TLA) is testnet for foo.alice.testnet
const TLA_MIN_LENGTH = 32;
const eventtracking = require('../utils/eventtracking');

const createAccountCommand = {
command: 'create-account <accountId>',
Expand Down Expand Up @@ -74,6 +75,7 @@ const createAccountCommandDeprecated = {
async function createAccount(options) {
// NOTE: initialBalance is passed as part of config here, parsed in middleware/initial-balance
// periods are disallowed in top-level accounts and can only be used for subaccounts
await eventtracking.askForConsentIfNeeded(options, options.masterAccount);
const splitAccount = options.accountId.split('.');

const splitMaster = options.masterAccount.split('.');
Expand Down
2 changes: 1 addition & 1 deletion commands/generate-key.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ module.exports = {

console.log(`Key pair with ${argv.publicKey} public key for an account "${argv.accountId}"`);
})
};
};
3 changes: 3 additions & 0 deletions commands/repl.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const eventtracking = require('../utils/eventtracking');

module.exports = {
command: 'repl',
desc: 'launch interactive Node.js shell with NEAR connection available to use. The repl\'s initial context contains `nearAPI`, `near`and `account` if an accountId cli argument is provided. ' +
Expand All @@ -6,6 +8,7 @@ module.exports = {
' near repl --acountId bob\n > console.log(account)\n > .load script.js',
builder: (yargs) => yargs,
handler: async (argv) => {
await eventtracking.askForConsentIfNeeded(argv);
const repl = require('repl');
const context = repl.start('> ').context;
context.nearAPI = require('near-api-js');
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exports.clean = async function () {
};

exports.deploy = async function (options) {
await eventtracking.askForConsentIfNeeded(options);
console.log(
`Starting deployment. Account id: ${options.accountId}, node: ${options.nodeUrl}, helper: ${options.helperUrl}, file: ${options.wasmFile}`);

Expand Down Expand Up @@ -175,6 +176,7 @@ exports.login = async function (options) {
};

exports.viewAccount = async function (options) {
await eventtracking.askForConsentIfNeeded(options);
let near = await connect(options);
let account = await near.account(options.accountId);
let state = await account.state();
Expand All @@ -186,7 +188,6 @@ exports.viewAccount = async function (options) {
};

exports.deleteAccount = async function (options) {

console.log(
`Deleting account. Account id: ${options.accountId}, node: ${options.nodeUrl}, helper: ${options.helperUrl}, beneficiary: ${options.beneficiaryId}`);
const near = await connect(options);
Expand Down
84 changes: 76 additions & 8 deletions utils/eventtracking.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const uuid = require('uuid');

const TRACKING_ENABLED_KEY = 'trackingEnabled';
const TRACKING_SESSION_ID_KEY = 'trackingSessionId';
const TRACKING_ID_KEY = 'trackingAccountID';

const isGitPod = () => {
return !!process.env.GITPOD_WORKSPACE_URL;
Expand Down Expand Up @@ -40,6 +41,20 @@ const shouldTrack = (shellSettings) => {
);
};

const shouldTrackID = (shellSettings) => {
return (
TRACKING_ID_KEY in shellSettings &&
shellSettings[TRACKING_ID_KEY]
);
};

const shouldNotTrackID = (shellSettings) => {
return (
TRACKING_ID_KEY in shellSettings &&
!shellSettings[TRACKING_ID_KEY]
);
};

const track = async (eventType, eventProperties, options) => {
const shellSettings = settings.getShellSettings();
if (!shouldTrack(shellSettings)) {
Expand All @@ -60,10 +75,8 @@ const track = async (eventType, eventProperties, options) => {
};
Object.assign(mixPanelProperties, eventProperties);
await Promise.all([mixpanel.track(eventType, mixPanelProperties),
mixpanel.people.set_once({
distinct_id: isGitPod()
? getGitPodUserHash()
: shellSettings[TRACKING_SESSION_ID_KEY],
mixpanel.people.set(mixPanelProperties.distinct_id, {
deployed_contracts: 0,
network_id: options.networkId,
node_url: options.nodeUrl,
})]);
Expand All @@ -84,8 +97,8 @@ const getEventTrackingConsent = async () => {
for (let attempts = 0; attempts < 10; attempts++) {
const answer = await new Promise((resolve) => {
rl.question(
chalk`We would like to collect data on near-cli usage to improve developer experience.` +
chalk` We will never send private information. We only collect which commands are run via an anonymous identifier.` +
chalk`We would like to collect data on near-cli usage to improve developer experience. ` +
chalk` We will never send private information. We only collect which commands are run with attributes. ` +
chalk`{bold.yellow Would you like to opt in (y/n)? }`,
async (consentToEventTracking) => {
if (consentToEventTracking.toLowerCase() == 'y') {
Expand All @@ -109,7 +122,59 @@ const getEventTrackingConsent = async () => {
}
};

const askForConsentIfNeeded = async (options) => {
const getIdTrackingConsent = async () => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
try {
for (let attempts = 0; attempts < 10; attempts++) {
const answer = await new Promise((resolve) => {
rl.question(
chalk`We would like to help with your development journey with NEAR. ` +
chalk`We will ask you to share your account ID while using command. ` +
chalk`Note that your account ID and all associated on-chain transactions are already being recorded on public blockchain. ` +
chalk`{bold.yellow Would you like to share the account Id (y/n)? }`,
async (consentToEventTracking) => {
if (consentToEventTracking.toLowerCase() == 'y') {
resolve(true);
} else if (
consentToEventTracking.toLowerCase() == 'n'
) {
resolve(false);
}
resolve(undefined);
}
);
});
if (answer !== undefined) {
return answer;
}
}
return false; // If they can't figure it out in this many attempts, just opt out
} finally {
rl.close();
}
};

const askForId = async (options, masterAccount) => {
const shellSettings = settings.getShellSettings();
const accountID = masterAccount ? masterAccount : options.accountId;
if(shouldTrackID(shellSettings)){
const id = isGitPod() ? getGitPodUserHash() : shellSettings[TRACKING_SESSION_ID_KEY];
await Promise.all([
mixpanel.alias(accountID, id),
mixpanel.people.set(id, {account_id: accountID})
]);
} else if(shouldNotTrackID(shellSettings)){
return;
} else{
shellSettings[TRACKING_ID_KEY] = (await getIdTrackingConsent());
settings.saveShellSettings(shellSettings);
}
};

const askForConsentIfNeeded = async (options, masterAccount) => {
const shellSettings = settings.getShellSettings();
// if the appropriate option is not in settings, ask now and save settings.
if (!(TRACKING_ENABLED_KEY in shellSettings)) {
Expand All @@ -125,10 +190,13 @@ const askForConsentIfNeeded = async (options) => {
await track(module.exports.EVENT_ID_TRACKING_OPT_IN, {}, options);
}
}
await askForId(options, masterAccount);
};

const trackDeployedContract = async () => {
await mixpanel.people.increment({deployed_contracts: 1});
const shellSettings = settings.getShellSettings();
const id = isGitPod() ? getGitPodUserHash() : shellSettings[TRACKING_SESSION_ID_KEY];
await mixpanel.people.increment(id, 'deployed_contracts');
};

module.exports = {
Expand Down

0 comments on commit 9f50e49

Please sign in to comment.