-
Notifications
You must be signed in to change notification settings - Fork 324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add telemetry to companion #1117
Changes from all commits
dbde5bd
3e3dc16
829283e
2ab0bbe
1934425
faf72a8
3b83c17
f6b6081
e372e74
11d3b9b
4a0af8f
a256e05
798c5fe
8d4d6c8
4058da2
0e66ea9
090d700
d83de1a
ceb65b6
9db9b6e
15ab207
fa915c3
c19ba88
d334ec0
162aaa8
089b0d0
ee060c2
ea798df
dad1bfa
d2f9cab
0810499
7160856
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,6 @@ | |
/coverage | ||
/.nyc_output | ||
/add-on/manifest.json | ||
|
||
.DS_Store | ||
.vscode |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,39 +16,39 @@ const waitFor = (f, t) => pWaitFor(f, { interval: tickMs, timeout: t || Infinity | |
// wrapper for chrome.ipfs.* that gets us closer to ergonomics of promise-based browser.* | ||
export const brave = hasBraveChromeIpfs() | ||
? Object.freeze({ | ||
// This is the main check - returns true only in Brave and only when | ||
// feature flag is enabled brave://flags and can be used for high level UI | ||
// decisions such as showing custom node type on Preferences | ||
getIPFSEnabled: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.getIPFSEnabled)), | ||
|
||
// Obtains a string representation of the resolve method | ||
// method is one of the following strings: | ||
// "ask" uses a gateway but also prompts them to install a local node | ||
// "gateway" uses a gateway but also prompts them to install a local node | ||
// "local" uses a gateway but also prompts them to install a local node | ||
// "disabled" disabled by the user | ||
// "undefined" everything else (IPFS feature flag is not enabled, error etc) | ||
getResolveMethodType: async () => | ||
String(await promisifyBraveCheck(chrome.ipfs.getResolveMethodType)), | ||
|
||
// Obtains the config contents of the local IPFS node | ||
// Returns undefined if missing for any reason | ||
getConfig: async () => | ||
await promisifyBraveCheck(chrome.ipfs.getConfig), | ||
|
||
// Returns true if binary is present | ||
getExecutableAvailable: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.getExecutableAvailable)), | ||
|
||
// Attempts to start the daemon and returns true if finished | ||
launch: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.launch)), | ||
|
||
// Attempts to stop the daemon and returns true if finished | ||
shutdown: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.shutdown)) | ||
}) | ||
// This is the main check - returns true only in Brave and only when | ||
// feature flag is enabled brave://flags and can be used for high level UI | ||
// decisions such as showing custom node type on Preferences | ||
getIPFSEnabled: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.getIPFSEnabled)), | ||
|
||
// Obtains a string representation of the resolve method | ||
// method is one of the following strings: | ||
// "ask" uses a gateway but also prompts them to install a local node | ||
// "gateway" uses a gateway but also prompts them to install a local node | ||
// "local" uses a gateway but also prompts them to install a local node | ||
// "disabled" disabled by the user | ||
// "undefined" everything else (IPFS feature flag is not enabled, error etc) | ||
getResolveMethodType: async () => | ||
String(await promisifyBraveCheck(chrome.ipfs.getResolveMethodType)), | ||
|
||
// Obtains the config contents of the local IPFS node | ||
// Returns undefined if missing for any reason | ||
getConfig: async () => | ||
await promisifyBraveCheck(chrome.ipfs.getConfig), | ||
|
||
// Returns true if binary is present | ||
getExecutableAvailable: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.getExecutableAvailable)), | ||
|
||
// Attempts to start the daemon and returns true if finished | ||
launch: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.launch)), | ||
|
||
// Attempts to stop the daemon and returns true if finished | ||
shutdown: async () => | ||
Boolean(await promisifyBraveCheck(chrome.ipfs.shutdown)) | ||
}) | ||
Comment on lines
-19
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lint fixes only. Willing to do separately |
||
: undefined | ||
|
||
export async function init (browser, opts) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ import createRuntimeChecks from './runtime-checks.js' | |
import { createContextMenus, findValueForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress, contextMenuViewOnGateway, contextMenuCopyPermalink, contextMenuCopyCidAddress } from './context-menus.js' | ||
import { registerSubdomainProxy } from './http-proxy.js' | ||
import { runPendingOnInstallTasks } from './on-installed.js' | ||
import { handleConsentFromState, startSession, endSession, trackView } from './telemetry.js' | ||
const log = debug('ipfs-companion:main') | ||
log.error = debug('ipfs-companion:main:error') | ||
|
||
|
@@ -33,6 +34,7 @@ export default async function init () { | |
// INIT | ||
// =================================================================== | ||
let ipfs // ipfs-api instance | ||
/** @type {import('../types.js').CompanionState} */ | ||
let state // avoid redundant API reads by utilizing local cache of various states | ||
let dnslinkResolver | ||
let ipfsPathValidator | ||
|
@@ -55,8 +57,11 @@ export default async function init () { | |
runtime = await createRuntimeChecks(browser) | ||
state = initState(options) | ||
notify = createNotifier(getState) | ||
// ensure consent is set properly on app init | ||
handleConsentFromState(state) | ||
|
||
if (state.active) { | ||
startSession() | ||
// It's ok for this to fail, node might be unavailable or mis-configured | ||
try { | ||
ipfs = await initIpfsClient(browser, state) | ||
|
@@ -167,6 +172,15 @@ export default async function init () { | |
const result = validIpfsOrIpns(path) ? resolveToPublicUrl(path) : null | ||
return Promise.resolve({ pubGwUrlForIpfsOrIpnsPath: result }) | ||
} | ||
if (request.telemetry) { | ||
return Promise.resolve(onTelemetryMessage(request.telemetry, sender)) | ||
} | ||
} | ||
|
||
function onTelemetryMessage (request, sender) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏽 |
||
if (request.trackView) { | ||
return trackView(request.trackView) | ||
} | ||
} | ||
|
||
// PORTS (connection-based messaging) | ||
|
@@ -366,11 +380,11 @@ export default async function init () { | |
// https://github.com/ipfs-shipyard/ipfs-companion/issues/398 | ||
if (runtime.isFirefox && ipfsPathValidator.isIpfsPageActionsContext(url)) { | ||
if (sameGateway(url, state.gwURL) || sameGateway(url, state.apiURL)) { | ||
await browser.pageAction.setIcon({ tabId: tabId, path: '/icons/ipfs-logo-on.svg' }) | ||
await browser.pageAction.setTitle({ tabId: tabId, title: browser.i18n.getMessage('pageAction_titleIpfsAtCustomGateway') }) | ||
await browser.pageAction.setIcon({ tabId, path: '/icons/ipfs-logo-on.svg' }) | ||
await browser.pageAction.setTitle({ tabId, title: browser.i18n.getMessage('pageAction_titleIpfsAtCustomGateway') }) | ||
} else { | ||
await browser.pageAction.setIcon({ tabId: tabId, path: '/icons/ipfs-logo-off.svg' }) | ||
await browser.pageAction.setTitle({ tabId: tabId, title: browser.i18n.getMessage('pageAction_titleIpfsAtPublicGateway') }) | ||
await browser.pageAction.setIcon({ tabId, path: '/icons/ipfs-logo-off.svg' }) | ||
await browser.pageAction.setTitle({ tabId, title: browser.i18n.getMessage('pageAction_titleIpfsAtPublicGateway') }) | ||
} | ||
await browser.pageAction.show(tabId) | ||
} | ||
|
@@ -554,6 +568,8 @@ export default async function init () { | |
await registerSubdomainProxy(getState, runtime) | ||
shouldRestartIpfsClient = true | ||
shouldStopIpfsClient = !state.active | ||
// Any time the extension switches active state, start or stop the current session. | ||
state.active ? startSession() : endSession() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If state.active is changed, and state is true-ish, start session. Otherwise, end session. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @whizzzkid @lidel is there a better place to do this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for now this should be ok, in the future we might wanna have a message being broadcast and such listeners can subscribe to those. |
||
break | ||
case 'ipfsNodeType': | ||
if (change.oldValue !== braveNodeType && change.newValue === braveNodeType) { | ||
|
@@ -620,6 +636,8 @@ export default async function init () { | |
break | ||
} | ||
} | ||
// ensure consent is set properly on state changes | ||
handleConsentFromState(state) | ||
|
||
if ((state.active && shouldRestartIpfsClient) || shouldStopIpfsClient) { | ||
try { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import MetricsProvider from '@ipfs-shipyard/ignite-metrics/vanilla' | ||
import debug from 'debug' | ||
|
||
const log = debug('ipfs-companion:telemetry') | ||
|
||
const metricsProvider = new MetricsProvider({ | ||
appKey: '393f72eb264c28a1b59973da1e0a3938d60dc38a', | ||
autoTrack: false, | ||
storageProvider: null | ||
}) | ||
|
||
/** | ||
* | ||
* @param {import('../types.js').CompanionState} state | ||
* @returns {void} | ||
*/ | ||
export function handleConsentFromState (state) { | ||
const telemetryGroups = { | ||
minimal: state?.telemetryGroupMinimal || false, | ||
performance: state?.telemetryGroupPerformance || false, | ||
ux: state?.telemetryGroupUx || false, | ||
feedback: state?.telemetryGroupFeedback || false, | ||
location: state?.telemetryGroupLocation || false | ||
} | ||
for (const [groupName, isEnabled] of Object.entries(telemetryGroups)) { | ||
if (isEnabled) { | ||
log(`Adding consent for '${groupName}'`) | ||
metricsProvider.addConsent(groupName) | ||
} else { | ||
log(`Removing consent for '${groupName}'`) | ||
metricsProvider.removeConsent(groupName) | ||
} | ||
} | ||
} | ||
|
||
const ignoredViewsRegex = [] | ||
export function trackView (view) { | ||
log('trackView called for view: ', view) | ||
metricsProvider.trackView(view, ignoredViewsRegex) | ||
} | ||
|
||
export const startSession = (...args) => metricsProvider.startSession(...args) | ||
export const endSession = (...args) => metricsProvider.endSession(...args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SgtPooki to address
Need to remove these since we wont be using them for now