Add Logging Service and event fixes.
- Original NewRelicService lifted from edx-portal. - Fixed bug with snake case of event data. - Added error handling for logEvent. - Added tests for logEvent. ARCH-430
This commit is contained in:
@@ -20,5 +20,8 @@
|
||||
},
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
"globals": {
|
||||
"newrelic": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// This is the dev Webpack config. All settings here should prefer a fast build
|
||||
// time at the expense of creating larger, unoptimized bundles.
|
||||
|
||||
const Merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
@@ -10,12 +11,12 @@ const commonConfig = require('./webpack.common.config.js');
|
||||
module.exports = Merge.smart(commonConfig, {
|
||||
mode: 'development',
|
||||
devtool: 'eval-source-map',
|
||||
entry: [
|
||||
entry: {
|
||||
// enable react's custom hot dev client so we get errors reported in the browser
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
path.resolve(__dirname, '../src/analytics/segment.js'),
|
||||
path.resolve(__dirname, '../src/index.jsx'),
|
||||
],
|
||||
hot: require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
segment: path.resolve(__dirname, '../src/analytics/segment.js'),
|
||||
app: path.resolve(__dirname, '../src/index.jsx'),
|
||||
},
|
||||
module: {
|
||||
// Specify file-by-file rules to Webpack. Some file-types need a particular kind of loader.
|
||||
rules: [
|
||||
|
||||
@@ -5,6 +5,7 @@ const commonConfig = require('./webpack.common.config.js');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
const HtmlWebpackNewRelicPlugin = require('html-webpack-new-relic-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
@@ -145,5 +146,12 @@ module.exports = Merge.smart(commonConfig, {
|
||||
APPLE_APP_STORE_URL: null,
|
||||
GOOGLE_PLAY_URL: null,
|
||||
}),
|
||||
new HtmlWebpackNewRelicPlugin({
|
||||
// This plugin fixes an issue where the newrelic script will break if
|
||||
// not added directly to the HTML.
|
||||
// We use non empty strings as defaults here to prevent errors for empty configs
|
||||
license: process.env.NEW_RELIC_LICENSE_KEY || 'fake_app',
|
||||
applicationID: process.env.NEW_RELIC_APP_ID || 'fake_license',
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
179
package-lock.json
generated
179
package-lock.json
generated
@@ -2606,6 +2606,32 @@
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"@newrelic/koa": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@newrelic/koa/-/koa-1.0.8.tgz",
|
||||
"integrity": "sha512-kY//FlLQkGdUIKEeGJlyY3dJRU63EG77YIa48ACMGZxQbWRd3WZMikyft33f8XScTq6WpCDo9xa0viNo8zeYkg==",
|
||||
"requires": {
|
||||
"methods": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"@newrelic/native-metrics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@newrelic/native-metrics/-/native-metrics-4.1.0.tgz",
|
||||
"integrity": "sha512-7CZlKMLuaYQW7mV9qVyo9b9HVe2xBnyn+kkETRJoZGs5P7gdfv9AAE3RPhtOBUopTfbmc8ju7njYadjui9J1XA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.12.1",
|
||||
"semver": "^5.5.1"
|
||||
}
|
||||
},
|
||||
"@newrelic/superagent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@newrelic/superagent/-/superagent-1.0.2.tgz",
|
||||
"integrity": "sha512-B2fM48kfY+5L6pwk6Yt79yk1JzMWKu1wV73If2shAzMsujNqSA4P+mLKCnTthIuKlhE78OfB1MzSpRMeB7kgWw==",
|
||||
"requires": {
|
||||
"methods": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"@redux-saga/core": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.0.1.tgz",
|
||||
@@ -2722,6 +2748,11 @@
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"@tyriar/fibonacci-heap": {
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tyriar/fibonacci-heap/-/fibonacci-heap-2.0.9.tgz",
|
||||
"integrity": "sha512-bYuSNomfn4hu2tPiDN+JZtnzCpSpbJ/PNeulmocDy3xN2X5OkJL65zo6rPZp65cPPhLF9vfT/dgE+RtFRCSxOA=="
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz",
|
||||
@@ -2987,6 +3018,14 @@
|
||||
"integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==",
|
||||
"dev": true
|
||||
},
|
||||
"agent-base": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
|
||||
"integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
|
||||
"requires": {
|
||||
"es6-promisify": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"airbnb-prop-types": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.11.0.tgz",
|
||||
@@ -7672,6 +7711,19 @@
|
||||
"is-symbol": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz",
|
||||
"integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q=="
|
||||
},
|
||||
"es6-promisify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
|
||||
"requires": {
|
||||
"es6-promise": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@@ -9273,8 +9325,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@@ -9295,14 +9346,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -9317,20 +9366,17 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -9447,8 +9493,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -9460,7 +9505,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -9475,7 +9519,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -9483,14 +9526,12 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@@ -9509,7 +9550,6 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -9590,8 +9630,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -9603,7 +9642,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -9689,8 +9727,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@@ -9726,7 +9763,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -9746,7 +9782,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -9790,14 +9825,12 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10434,6 +10467,15 @@
|
||||
"mkdirp": "^0.5.1"
|
||||
}
|
||||
},
|
||||
"html-webpack-new-relic-plugin": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-new-relic-plugin/-/html-webpack-new-relic-plugin-1.0.1.tgz",
|
||||
"integrity": "sha512-BT0Un4Ykp5jwed1hE61zqtm2PKGddufbQZ1om01HItW6bJ8arz5MeDb6PZWmtznNYbM9x8NqSdmvfUC+dWivLg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cheerio": "^1.0.0-rc.2"
|
||||
}
|
||||
},
|
||||
"html-webpack-plugin": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
|
||||
@@ -10857,6 +10899,15 @@
|
||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
||||
"dev": true
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
|
||||
"integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
|
||||
"requires": {
|
||||
"agent-base": "^4.1.0",
|
||||
"debug": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"humps": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
|
||||
@@ -12561,8 +12612,7 @@
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
|
||||
"dev": true
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"json3": {
|
||||
"version": "3.3.2",
|
||||
@@ -13202,8 +13252,7 @@
|
||||
"methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
|
||||
"dev": true
|
||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "2.3.11",
|
||||
@@ -13490,8 +13539,7 @@
|
||||
"nan": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
|
||||
"integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw=="
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
@@ -13571,6 +13619,62 @@
|
||||
"integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
|
||||
"dev": true
|
||||
},
|
||||
"newrelic": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/newrelic/-/newrelic-5.5.0.tgz",
|
||||
"integrity": "sha512-/grE+surOis796SHdDjQD+iWEcQ+r6p4eVRxEoQTvDg4wcmEklcvSTVyyyqdO2Nt8KCcM5CD+szBKg3RPuQIIg==",
|
||||
"requires": {
|
||||
"@newrelic/koa": "^1.0.8",
|
||||
"@newrelic/native-metrics": "^4.0.0",
|
||||
"@newrelic/superagent": "^1.0.2",
|
||||
"@tyriar/fibonacci-heap": "^2.0.7",
|
||||
"async": "^2.1.4",
|
||||
"concat-stream": "^2.0.0",
|
||||
"https-proxy-agent": "^2.2.1",
|
||||
"json-stringify-safe": "^5.0.0",
|
||||
"readable-stream": "^3.1.1",
|
||||
"semver": "^5.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
|
||||
"integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.11"
|
||||
}
|
||||
},
|
||||
"concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz",
|
||||
"integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
|
||||
"integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nice-try": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||
@@ -19299,8 +19403,7 @@
|
||||
"semver": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
|
||||
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
|
||||
},
|
||||
"semver-diff": {
|
||||
"version": "2.1.0",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"iso-countries-languages": "^0.2.1",
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"lodash.snakecase": "^4.1.1",
|
||||
"newrelic": "^5.5.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"query-string": "^5.1.1",
|
||||
"react": "^16.8.3",
|
||||
@@ -88,6 +89,7 @@
|
||||
"fetch-mock": "^6.3.0",
|
||||
"file-loader": "^1.1.9",
|
||||
"html-webpack-harddisk-plugin": "^0.2.0",
|
||||
"html-webpack-new-relic-plugin": "^1.0.1",
|
||||
"html-webpack-plugin": "^3.0.3",
|
||||
"husky": "^0.14.3",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import apiClient from '../config/apiClient';
|
||||
import { configuration } from '../config/environment';
|
||||
import { snakeCaseObject } from '../services/utils';
|
||||
import LoggingService from '../services/LoggingService';
|
||||
|
||||
const eventLogApiBaseUrl = `${configuration.LMS_BASE_URL}/event`;
|
||||
|
||||
@@ -14,14 +15,16 @@ function handleTrackEvents(eventName, properties) {
|
||||
// Sends events to tracking log and downstream
|
||||
// TODO: Determine consistent naming for eventName vs eventType and properties v eventData.
|
||||
function logEvent(eventType, eventData) {
|
||||
snakeCaseObject(eventData, { deep: true });
|
||||
const snakeEventData = snakeCaseObject(eventData, { deep: true });
|
||||
const serverData = {
|
||||
event_type: eventType,
|
||||
event: eventData,
|
||||
event: snakeEventData,
|
||||
page: window.location.href,
|
||||
};
|
||||
// TODO: ARCH-430: Send errors to New Relic.
|
||||
return apiClient.post(eventLogApiBaseUrl, serverData);
|
||||
return apiClient.post(eventLogApiBaseUrl, serverData)
|
||||
.catch((error) => {
|
||||
LoggingService.logAPIErrorResponse(error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
57
src/analytics/analytics.test.js
Normal file
57
src/analytics/analytics.test.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { logEvent } from './analytics';
|
||||
import { configuration } from '../config/environment';
|
||||
import LoggingService from '../services/LoggingService';
|
||||
import apiClient from '../config/apiClient';
|
||||
|
||||
jest.mock('../services/LoggingService');
|
||||
jest.mock('../config/apiClient');
|
||||
|
||||
const eventType = 'test.event';
|
||||
const eventData = {
|
||||
testShallow: 'test-shallow',
|
||||
testObject: {
|
||||
testDeep: 'test-deep',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
beforeAll(() => {
|
||||
apiClient.mockClear();
|
||||
LoggingService.mockClear();
|
||||
});
|
||||
|
||||
|
||||
describe('analytics logEvent', () => {
|
||||
it('posts expected data when successful', () => {
|
||||
jest.spyOn(apiClient, 'post').mockResolvedValue(undefined);
|
||||
|
||||
expect.assertions(3);
|
||||
return logEvent(eventType, eventData)
|
||||
.then(() => {
|
||||
expect(apiClient.post.mock.calls.length).toEqual(1);
|
||||
expect(apiClient.post.mock.calls[0][0]).toEqual(`${configuration.LMS_BASE_URL}/event`);
|
||||
expect(apiClient.post.mock.calls[0][1]).toEqual({
|
||||
event_type: 'test.event',
|
||||
event: {
|
||||
test_shallow: 'test-shallow',
|
||||
test_object: {
|
||||
test_deep: 'test-deep',
|
||||
},
|
||||
},
|
||||
page: window.location.href,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('calls LoggingService.logAPIErrorResponse on error', () => {
|
||||
LoggingService.logAPIErrorResponse = jest.fn();
|
||||
jest.spyOn(apiClient, 'post').mockRejectedValue('test-error');
|
||||
|
||||
expect.assertions(2);
|
||||
return logEvent(eventType, eventData)
|
||||
.then(() => {
|
||||
expect(LoggingService.logAPIErrorResponse.mock.calls.length).toBe(1);
|
||||
expect(LoggingService.logAPIErrorResponse.mock.calls[0][0]).toEqual('test-error');
|
||||
});
|
||||
});
|
||||
});
|
||||
32
src/services/LoggingService.js
Normal file
32
src/services/LoggingService.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Logs info and errors to NewRelic and console.
|
||||
*
|
||||
* Requires the NewRelic Browser JavaScript snippet.
|
||||
*/
|
||||
class LoggingService {
|
||||
static logInfo(message) {
|
||||
if (typeof newrelic !== 'undefined') {
|
||||
newrelic.addPageAction('INFO', { message });
|
||||
}
|
||||
}
|
||||
|
||||
static logError(error) {
|
||||
if (typeof newrelic !== 'undefined') {
|
||||
newrelic.noticeError(error);
|
||||
}
|
||||
}
|
||||
|
||||
static logAPIErrorResponse(error) {
|
||||
let { message } = error;
|
||||
if (error.response) {
|
||||
message = `${error.response.status} ${error.response.config.url} ${JSON.stringify(error.response.data)}`;
|
||||
} else if (error.request) {
|
||||
message = `${error.request.status} ${error.request.responseURL} ${error.request.responseText}`;
|
||||
} else if (error.stack) {
|
||||
message = error.stack;
|
||||
}
|
||||
this.logError(new Error(`API request failed: ${message}`));
|
||||
}
|
||||
}
|
||||
|
||||
export default LoggingService;
|
||||
66
src/services/LoggingService.test.js
Normal file
66
src/services/LoggingService.test.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import LoggingService from './LoggingService';
|
||||
|
||||
global.newrelic = {
|
||||
addPageAction: jest.fn(),
|
||||
noticeError: jest.fn(),
|
||||
};
|
||||
|
||||
describe('logInfo', () => {
|
||||
it('calls New Relic client to log message if the client is available', () => {
|
||||
const message = 'Test log';
|
||||
LoggingService.logInfo(message);
|
||||
expect(global.newrelic.addPageAction).toHaveBeenCalledWith('INFO', { message });
|
||||
});
|
||||
});
|
||||
|
||||
describe('logError', () => {
|
||||
it('calls New Relic client to log error if the client is available', () => {
|
||||
const error = new Error('Failed!');
|
||||
LoggingService.logError(error);
|
||||
expect(global.newrelic.noticeError).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('logAPIErrorResponse', () => {
|
||||
it('calls New Relic client to log error when error has request object', () => {
|
||||
const error = {
|
||||
request: {
|
||||
status: 400,
|
||||
responseURL: 'http://example.com',
|
||||
responseText: 'Very bad request',
|
||||
},
|
||||
};
|
||||
const message = `${error.request.status} ${error.request.responseURL} ${error.request.responseText}`;
|
||||
const expectedError = new Error(`API request failed: ${message}`);
|
||||
LoggingService.logAPIErrorResponse(error);
|
||||
expect(global.newrelic.noticeError).toHaveBeenCalledWith(expectedError);
|
||||
});
|
||||
it('calls New Relic client to log error when error has response object', () => {
|
||||
const error = {
|
||||
response: {
|
||||
status: 400,
|
||||
config: {
|
||||
url: 'http://example.com',
|
||||
},
|
||||
data: {
|
||||
detail: 'Very bad request',
|
||||
},
|
||||
},
|
||||
};
|
||||
const message = `${error.response.status} ${error.response.config.url} ${JSON.stringify(error.response.data)}`;
|
||||
const expectedError = new Error(`API request failed: ${message}`);
|
||||
LoggingService.logAPIErrorResponse(error);
|
||||
expect(global.newrelic.noticeError).toHaveBeenCalledWith(expectedError);
|
||||
});
|
||||
it('calls New Relic client to log error when error has stack object', () => {
|
||||
const error = {
|
||||
stack: `TypeError: Cannot read property 'uuid' of undefined
|
||||
at portalConfiguration (webpack:///./src/data/reducers/portalConfiguration.js?:35:43)
|
||||
at combination (webpack:///./node_modules/redux/es/combineReducers.js?:125:29)
|
||||
at dispatch (webpack:///./node_modules/redux/es/createStore.js?:170:22)`,
|
||||
};
|
||||
const expectedError = new Error(`API request failed: ${error.stack}`);
|
||||
LoggingService.logAPIErrorResponse(error);
|
||||
expect(global.newrelic.noticeError).toHaveBeenCalledWith(expectedError);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user