mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Add mock Segment server (#9783)
This mock Segment server can be used to test our extension metrics. It will respond to all request with HTTP 200, and will print the requests to the console. It also has parsing built-in for Segment request payloads. Right now only the event name is printed, but we can enhance this in the future to print more event information. We can also enhance the mock to be a more realistic representation of the API. The extension has been modified to allow the Segment host to be overwritten with the `SEGMENT_HOST` environment variable. This will ensure that all Segment events are redirected to that host. So for example, to create a dev build that uses this server, you could set the `SEGMENT_WRITE_KEY` and `SEGMENT_LEGACY_WRITE_KEY` values to any non-empty string, and set `SEGMENT_HOST` to `http://localhost:9090`. This was created originally to test PR #9768
This commit is contained in:
parent
5e61955d99
commit
4cdf251ea5
@ -17,6 +17,7 @@ const { makeStringTransform } = require('browserify-transform-tools')
|
||||
|
||||
const conf = require('rc')('metamask', {
|
||||
INFURA_PROJECT_ID: process.env.INFURA_PROJECT_ID,
|
||||
SEGMENT_HOST: process.env.SEGMENT_HOST,
|
||||
SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,
|
||||
SEGMENT_LEGACY_WRITE_KEY: process.env.SEGMENT_LEGACY_WRITE_KEY,
|
||||
})
|
||||
@ -372,6 +373,7 @@ function createScriptTasks({ browserPlatforms, livereload }) {
|
||||
INFURA_PROJECT_ID: opts.testing
|
||||
? '00000000000000000000000000000000'
|
||||
: conf.INFURA_PROJECT_ID,
|
||||
SEGMENT_HOST: conf.SEGMENT_HOST,
|
||||
// When we're in the 'production' environment we will use a specific key only set in CI
|
||||
// Otherwise we'll use the key from .metamaskrc or from the environment variable. If
|
||||
// the value of SEGMENT_WRITE_KEY that we envify is undefined then no events will be tracked
|
||||
|
93
development/lib/create-segment-server.js
Normal file
93
development/lib/create-segment-server.js
Normal file
@ -0,0 +1,93 @@
|
||||
const http = require('http')
|
||||
|
||||
/**
|
||||
* This is the default error handler to be used by this mock segment server.
|
||||
* It will print the error to the console and exit the process.
|
||||
*
|
||||
* @param {Error} error - The server error
|
||||
*/
|
||||
function defaultOnError(error) {
|
||||
console.log(error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('http').IncomingMessage} IncomingMessage
|
||||
* @typedef {import('http').ServerResponse} ServerResponse
|
||||
*/
|
||||
|
||||
/**
|
||||
* This function handles requests for the mock Segment server
|
||||
* @typedef {(request: IncomingMessage, response: ServerResponse, metricEvents: Array<Object>) => void} MockSegmentRequestHandler
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a HTTP server that acts as a fake version of the Segment API.
|
||||
* The bevahiour is rudimentary at the moment - it returns HTTP 200 in response
|
||||
* to every request. The only function this serves is to spy on requests sent to
|
||||
* this server, and to parse the request payloads as Segment batch events.
|
||||
*
|
||||
* @param {MockSegmentRequestHandler} onRequest- A callback for each request the server receives.
|
||||
* @param {(error: Error) => void} [onError] - A callback for server error events
|
||||
*/
|
||||
function createSegmentServer(onRequest, onError = defaultOnError) {
|
||||
const server = http.createServer(async (request, response) => {
|
||||
const chunks = []
|
||||
|
||||
request.on('data', (chunk) => {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
|
||||
await new Promise((resolve) => {
|
||||
request.on('end', () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
// respond to preflight request
|
||||
if (request.method === 'OPTIONS') {
|
||||
response.setHeader('Access-Control-Allow-Origin', '*')
|
||||
response.setHeader('Access-Control-Allow-Methods', '*')
|
||||
response.setHeader('Access-Control-Allow-Headers', '*')
|
||||
response.statusCode = 200
|
||||
response.end()
|
||||
return
|
||||
}
|
||||
|
||||
let metricEvents = []
|
||||
if (chunks.length) {
|
||||
const body = Buffer.concat(chunks).toString()
|
||||
const segmentPayload = JSON.parse(body)
|
||||
metricEvents = segmentPayload.batch
|
||||
}
|
||||
|
||||
onRequest(request, response, metricEvents)
|
||||
})
|
||||
|
||||
server.on('error', onError)
|
||||
|
||||
return {
|
||||
start: async (port) => {
|
||||
await new Promise((resolve, reject) => {
|
||||
server.listen(port, (error) => {
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
stop: async () => {
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close((error) => {
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { createSegmentServer }
|
20
development/lib/parse-port.js
Normal file
20
development/lib/parse-port.js
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Parse a string as a port number. Non-integers or invalid ports will
|
||||
* result in an error being thrown.
|
||||
*
|
||||
* @param {String} portString - The string to parse as a port number
|
||||
* @returns {number} The parsed port number
|
||||
*/
|
||||
function parsePort(portString) {
|
||||
const port = Number(portString)
|
||||
if (!Number.isInteger(port)) {
|
||||
throw new Error(`Port '${portString}' is invalid; must be an integer`)
|
||||
} else if (port < 0 || port > 65535) {
|
||||
throw new Error(
|
||||
`Port '${portString}' is out of range; must be between 0 and 65535 inclusive`,
|
||||
)
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
module.exports = { parsePort }
|
68
development/mock-segment.js
Normal file
68
development/mock-segment.js
Normal file
@ -0,0 +1,68 @@
|
||||
const { createSegmentServer } = require('./lib/create-segment-server')
|
||||
const { parsePort } = require('./lib/parse-port')
|
||||
|
||||
const DEFAULT_PORT = 9090
|
||||
const prefix = '[mock-segment]'
|
||||
|
||||
function onRequest(request, response, events) {
|
||||
console.log(`${prefix}: ${request.method} ${request.url}`)
|
||||
const eventDescriptions = events.map((event) => {
|
||||
if (event.type === 'track') {
|
||||
return event.event
|
||||
} else if (event.type === 'page') {
|
||||
return event.name
|
||||
}
|
||||
return `[Unrecognized event type: ${event.type}]`
|
||||
})
|
||||
console.log(`${prefix}: Events received: ${eventDescriptions.join(', ')}`)
|
||||
|
||||
response.statusCode = 200
|
||||
response.end()
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a mock Segment API meant to be run from the command line. It will start a server
|
||||
* with the port specified, and respond with HTTP 200 to all requests. Any requests will be
|
||||
* logged to the console, along with the parsed Segment events included in the request (if
|
||||
* any)
|
||||
*
|
||||
* This can be used with the MetaMask extension by setting the `SEGMENT_HOST` environment
|
||||
* variable or config entry when building MetaMask.
|
||||
*
|
||||
* For example, to build MetaMask for use with this mock Segment server, you could set the
|
||||
* following values in `.metamaskrc` before building:
|
||||
*
|
||||
* SEGMENT_HOST='http://localhost:9090'
|
||||
* SEGMENT_WRITE_KEY=FAKE
|
||||
* SEGMENT_LEGACY_WRITE_KEY=FAKE
|
||||
*
|
||||
* Note that the Segment keys must also be set - otherwise the extension will not send any
|
||||
* metric events.
|
||||
*/
|
||||
const main = async () => {
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
let port = process.env.port || DEFAULT_PORT
|
||||
|
||||
while (args.length) {
|
||||
if (/^(--port|-p)$/u.test(args[0])) {
|
||||
if (args[1] === undefined) {
|
||||
throw new Error('Missing port argument')
|
||||
}
|
||||
port = parsePort(args[1])
|
||||
args.splice(0, 2)
|
||||
}
|
||||
}
|
||||
|
||||
const server = createSegmentServer(onRequest, onError)
|
||||
|
||||
await server.start(port)
|
||||
console.log(`${prefix}: Listening on port ${port}`)
|
||||
}
|
||||
|
||||
main().catch(onError)
|
@ -5,6 +5,7 @@ const chalk = require('chalk')
|
||||
const pify = require('pify')
|
||||
|
||||
const createStaticServer = require('./create-static-server')
|
||||
const { parsePort } = require('./lib/parse-port')
|
||||
|
||||
const fsStat = pify(fs.stat)
|
||||
const DEFAULT_PORT = 9080
|
||||
@ -37,18 +38,6 @@ const startServer = ({ port, rootDirectory }) => {
|
||||
})
|
||||
}
|
||||
|
||||
const parsePort = (portString) => {
|
||||
const port = Number(portString)
|
||||
if (!Number.isInteger(port)) {
|
||||
throw new Error(`Port '${portString}' is invalid; must be an integer`)
|
||||
} else if (port < 0 || port > 65535) {
|
||||
throw new Error(
|
||||
`Port '${portString}' is out of range; must be between 0 and 65535 inclusive`,
|
||||
)
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
const parseDirectoryArgument = async (pathString) => {
|
||||
const resolvedPath = path.resolve(pathString)
|
||||
const directoryStats = await fsStat(resolvedPath)
|
||||
|
@ -59,19 +59,31 @@ export function sendCountIsTrackable(sendCount) {
|
||||
return Boolean(trackableSendCounts[sendCount])
|
||||
}
|
||||
|
||||
const isDevOrTestEnvironment = Boolean(
|
||||
process.env.METAMASK_DEBUG || process.env.IN_TEST,
|
||||
)
|
||||
|
||||
// This allows us to overwrite the metric destination for testing purposes
|
||||
const host = process.env.SEGMENT_HOST ?? undefined
|
||||
|
||||
// We do not want to track events on development builds unless specifically
|
||||
// provided a SEGMENT_WRITE_KEY. This also holds true for test environments and
|
||||
// E2E, which is handled in the build process by never providing the SEGMENT_WRITE_KEY
|
||||
// when process.env.IN_TEST is truthy
|
||||
export const segment =
|
||||
process.env.IN_TEST || !process.env.SEGMENT_WRITE_KEY
|
||||
!process.env.SEGMENT_WRITE_KEY || (isDevOrTestEnvironment && !host)
|
||||
? segmentNoop
|
||||
: new Analytics(process.env.SEGMENT_WRITE_KEY, { flushAt, flushInterval })
|
||||
: new Analytics(process.env.SEGMENT_WRITE_KEY, {
|
||||
host,
|
||||
flushAt,
|
||||
flushInterval,
|
||||
})
|
||||
|
||||
export const segmentLegacy =
|
||||
process.env.IN_TEST || !process.env.SEGMENT_LEGACY_WRITE_KEY
|
||||
!process.env.SEGMENT_LEGACY_WRITE_KEY || (isDevOrTestEnvironment && !host)
|
||||
? segmentNoop
|
||||
: new Analytics(process.env.SEGMENT_LEGACY_WRITE_KEY, {
|
||||
host,
|
||||
flushAt,
|
||||
flushInterval,
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user