mirror of
https://github.com/mattermost/focalboard.git
synced 2024-11-27 08:31:20 +02:00
Plugin telemetry (#1069)
* implement webapp telemetry * cleanup * remove imports, update events * change event title * update for lint * add test, update filename * linter fix * fix field name * revert changes * fix test * update builds * fix workflows * fix workflows * fix workflow * temp checkin * remove log lines * updates from peer review
This commit is contained in:
parent
59b8ae4517
commit
94e6e8a9f7
10
.github/workflows/build-mac.yml
vendored
10
.github/workflows/build-mac.yml
vendored
@ -12,12 +12,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: npm install
|
||||
run: cd webapp; npm install --no-optional
|
||||
|
||||
|
10
.github/workflows/build-ubuntu.yml
vendored
10
.github/workflows/build-ubuntu.yml
vendored
@ -11,12 +11,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: npm install
|
||||
run: cd webapp; npm install --no-optional
|
||||
|
||||
|
10
.github/workflows/build-win-wpf.yml
vendored
10
.github/workflows/build-win-wpf.yml
vendored
@ -11,12 +11,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
|
||||
|
40
.github/workflows/dev-release.yml
vendored
40
.github/workflows/dev-release.yml
vendored
@ -16,12 +16,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: npm install
|
||||
run: cd webapp; npm install --no-optional
|
||||
|
||||
@ -69,12 +75,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: npm install
|
||||
run: cd webapp; npm install --no-optional
|
||||
|
||||
@ -105,12 +117,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
|
||||
@ -154,12 +172,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_DEV_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: npm install
|
||||
run: cd webapp; npm install --no-optional
|
||||
|
||||
|
10
.github/workflows/plugin-release.yml
vendored
10
.github/workflows/plugin-release.yml
vendored
@ -11,12 +11,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_PROD_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_PROD_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: npm install
|
||||
run: cd webapp; npm install --no-optional
|
||||
|
||||
|
10
.github/workflows/prod-release.yml
vendored
10
.github/workflows/prod-release.yml
vendored
@ -11,12 +11,18 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Replace token 1
|
||||
- name: Replace token 1 server
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2
|
||||
- name: Replace token 1 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: Replace token 2 server
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_PROD_KEY }},g" ${{ github.workspace }}/server/services/telemetry/telemetry.go
|
||||
|
||||
- name: Replace token 2 webapp
|
||||
run: sed -i -e "s,placeholder_rudder_key,${{ secrets.RUDDER_PROD_KEY }},g" ${{ github.workspace }}/mattermost-plugin/webapp/src/index.tsx
|
||||
|
||||
- name: npm install
|
||||
run: cd webapp; npm install --no-optional
|
||||
|
||||
|
@ -88,6 +88,13 @@ func (p *Plugin) OnActivate() error {
|
||||
baseURL = *mmconfig.ServiceSettings.SiteURL
|
||||
}
|
||||
|
||||
serverID := client.System.GetDiagnosticID()
|
||||
|
||||
enableTelemetry := false
|
||||
if mmconfig.LogSettings.EnableDiagnostics != nil {
|
||||
enableTelemetry = *mmconfig.LogSettings.EnableDiagnostics
|
||||
}
|
||||
|
||||
cfg := &config.Configuration{
|
||||
ServerRoot: baseURL + "/plugins/focalboard",
|
||||
Port: -1,
|
||||
@ -100,7 +107,8 @@ func (p *Plugin) OnActivate() error {
|
||||
FilesDriver: *mmconfig.FileSettings.DriverName,
|
||||
FilesPath: *mmconfig.FileSettings.Directory,
|
||||
FilesS3Config: filesS3Config,
|
||||
Telemetry: true,
|
||||
Telemetry: enableTelemetry,
|
||||
TelemetryID: serverID,
|
||||
WebhookUpdate: []string{},
|
||||
SessionExpireTime: 2592000,
|
||||
SessionRefreshTime: 18000,
|
||||
@ -122,7 +130,6 @@ func (p *Plugin) OnActivate() error {
|
||||
db = layeredStore
|
||||
}
|
||||
|
||||
serverID := client.System.GetDiagnosticID()
|
||||
p.wsPluginAdapter = ws.NewPluginAdapter(p.API, auth.New(cfg, db))
|
||||
|
||||
server, err := server.New(cfg, "", db, logger, serverID, p.wsPluginAdapter)
|
||||
|
@ -5,6 +5,8 @@ import {Store, Action} from 'redux'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {useHistory} from 'mm-react-router-dom'
|
||||
|
||||
import {rudderAnalytics, RudderTelemetryHandler} from 'mattermost-redux/client/rudder'
|
||||
|
||||
import {GlobalState} from 'mattermost-redux/types/store'
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences'
|
||||
|
||||
@ -20,9 +22,12 @@ import FocalboardIcon from '../../../webapp/src/widgets/icons/logo'
|
||||
import {setMattermostTheme} from '../../../webapp/src/theme'
|
||||
import wsClient, {MMWebSocketClient, ACTION_UPDATE_BLOCK} from './../../../webapp/src/wsclient'
|
||||
|
||||
import TelemetryClient from '../../../webapp/src/telemetry/telemetryClient'
|
||||
|
||||
import '../../../webapp/src/styles/focalboard-variables.scss'
|
||||
import '../../../webapp/src/styles/main.scss'
|
||||
import '../../../webapp/src/styles/labels.scss'
|
||||
import octoClient from '../../../webapp/src/octoClient'
|
||||
|
||||
import manifest from './manifest'
|
||||
import ErrorBoundary from './error_boundary'
|
||||
@ -32,6 +37,22 @@ import {PluginRegistry} from './types/mattermost-webapp'
|
||||
|
||||
import './plugin.scss'
|
||||
|
||||
const TELEMETRY_RUDDER_KEY = 'placeholder_rudder_key'
|
||||
const TELEMETRY_RUDDER_DATAPLANE_URL = 'placeholder_rudder_dataplane_url'
|
||||
const TELEMETRY_OPTIONS = {
|
||||
context: {
|
||||
ip: '0.0.0.0',
|
||||
},
|
||||
page: {
|
||||
path: '',
|
||||
referrer: '',
|
||||
search: '',
|
||||
title: '',
|
||||
url: '',
|
||||
},
|
||||
anonymousId: '00000000000000000000000000',
|
||||
}
|
||||
|
||||
type Props = {
|
||||
webSocketClient: MMWebSocketClient
|
||||
}
|
||||
@ -150,6 +171,32 @@ export default class Plugin {
|
||||
this.registry.registerCustomRoute('/', MainApp)
|
||||
}
|
||||
|
||||
const config = await octoClient.getClientConfig()
|
||||
if (config?.telemetry) {
|
||||
let rudderKey = TELEMETRY_RUDDER_KEY
|
||||
let rudderUrl = TELEMETRY_RUDDER_DATAPLANE_URL
|
||||
|
||||
if (rudderKey.startsWith('placeholder') && rudderUrl.startsWith('placeholder')) {
|
||||
rudderKey = process.env.RUDDER_KEY as string //eslint-disable-line no-process-env
|
||||
rudderUrl = process.env.RUDDER_DATAPLANE_URL as string //eslint-disable-line no-process-env
|
||||
}
|
||||
|
||||
if (rudderKey !== '') {
|
||||
rudderAnalytics.load(rudderKey, rudderUrl)
|
||||
|
||||
rudderAnalytics.identify(config?.telemetryid, {}, TELEMETRY_OPTIONS)
|
||||
|
||||
rudderAnalytics.page('BoardsLoaded', '',
|
||||
TELEMETRY_OPTIONS.page,
|
||||
{
|
||||
context: TELEMETRY_OPTIONS.context,
|
||||
anonymousId: TELEMETRY_OPTIONS.anonymousId,
|
||||
})
|
||||
|
||||
TelemetryClient.setTelemetryHandler(new RudderTelemetryHandler())
|
||||
}
|
||||
}
|
||||
|
||||
// register websocket handlers
|
||||
this.registry?.registerWebSocketEventHandler(`custom_${manifest.id}_${ACTION_UPDATE_BLOCK}`, (e: any) => wsClient.updateBlockHandler(e.data))
|
||||
}
|
||||
|
@ -143,3 +143,11 @@ module.exports = {
|
||||
mode,
|
||||
plugins,
|
||||
};
|
||||
|
||||
const env = {};
|
||||
env.RUDDER_KEY = JSON.stringify(process.env.RUDDER_KEY || ''); //eslint-disable-line no-process-env
|
||||
env.RUDDER_DATAPLANE_URL = JSON.stringify(process.env.RUDDER_DATAPLANE_URL || ''); //eslint-disable-line no-process-env
|
||||
|
||||
module.exports.plugins.push(new webpack.DefinePlugin({
|
||||
'process.env': env,
|
||||
}));
|
||||
|
@ -90,6 +90,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
|
||||
|
||||
apiv1.HandleFunc("/login", a.handleLogin).Methods("POST")
|
||||
apiv1.HandleFunc("/register", a.handleRegister).Methods("POST")
|
||||
apiv1.HandleFunc("/clientConfig", a.getClientConfig).Methods("GET")
|
||||
|
||||
apiv1.HandleFunc("/workspaces/{workspaceID}/{rootID}/files", a.sessionRequired(a.handleUploadFile)).Methods("POST")
|
||||
|
||||
@ -115,6 +116,17 @@ func (a *API) requireCSRFToken(next http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func (a *API) getClientConfig(w http.ResponseWriter, r *http.Request) {
|
||||
clientConfig := a.app.GetClientConfig()
|
||||
|
||||
configData, err := json.Marshal(clientConfig)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
jsonBytesResponse(w, http.StatusOK, configData)
|
||||
}
|
||||
|
||||
func (a *API) checkCSRFToken(r *http.Request) bool {
|
||||
token := r.Header.Get(HeaderRequestedWith)
|
||||
return token == HeaderRequestedWithXML
|
||||
|
12
server/app/clientConfig.go
Normal file
12
server/app/clientConfig.go
Normal file
@ -0,0 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/mattermost/focalboard/server/model"
|
||||
)
|
||||
|
||||
func (a *App) GetClientConfig() *model.ClientConfig {
|
||||
return &model.ClientConfig{
|
||||
Telemetry: a.config.Telemetry,
|
||||
TelemetryID: a.config.TelemetryID,
|
||||
}
|
||||
}
|
6
server/model/clientConfig.go
Normal file
6
server/model/clientConfig.go
Normal file
@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
type ClientConfig struct {
|
||||
Telemetry bool `json:"telemetry"`
|
||||
TelemetryID string `json:"telemetryid"`
|
||||
}
|
@ -38,6 +38,7 @@ type Configuration struct {
|
||||
FilesS3Config AmazonS3Config `json:"filess3config" mapstructure:"filess3config"`
|
||||
FilesPath string `json:"filespath" mapstructure:"filespath"`
|
||||
Telemetry bool `json:"telemetry" mapstructure:"telemetry"`
|
||||
TelemetryID string `json:"telemetryid" mapstructure:"telemetryid"`
|
||||
PrometheusAddress string `json:"prometheus_address" mapstructure:"prometheus_address"`
|
||||
WebhookUpdate []string `json:"webhook_update" mapstructure:"webhook_update"`
|
||||
Secret string `json:"secret" mapstructure:"secret"`
|
||||
@ -73,6 +74,7 @@ func ReadConfigFile() (*Configuration, error) {
|
||||
viper.SetDefault("FilesPath", "./files")
|
||||
viper.SetDefault("FilesDriver", "local")
|
||||
viper.SetDefault("Telemetry", true)
|
||||
viper.SetDefault("TelemetryID", "")
|
||||
viper.SetDefault("WebhookUpdate", nil)
|
||||
viper.SetDefault("SessionExpireTime", 60*60*24*30) // 30 days session lifetime
|
||||
viper.SetDefault("SessionRefreshTime", 60*60*5) // 5 minutes session refresh
|
||||
|
@ -12,6 +12,8 @@ import {DndProvider} from 'react-dnd'
|
||||
import {HTML5Backend} from 'react-dnd-html5-backend'
|
||||
import {TouchBackend} from 'react-dnd-touch-backend'
|
||||
|
||||
import TelemetryClient from './telemetry/telemetryClient'
|
||||
|
||||
import {getMessages} from './i18n'
|
||||
import {FlashMessages} from './components/flashMessages'
|
||||
import BoardPage from './pages/boardPage'
|
||||
@ -22,15 +24,18 @@ import LoginPage from './pages/loginPage'
|
||||
import RegisterPage from './pages/registerPage'
|
||||
import {Utils} from './utils'
|
||||
import wsClient from './wsclient'
|
||||
import {fetchMe, getLoggedIn} from './store/users'
|
||||
import {fetchMe, getLoggedIn, getMe} from './store/users'
|
||||
import {getLanguage, fetchLanguage} from './store/language'
|
||||
import {setGlobalError, getGlobalError} from './store/globalError'
|
||||
import {useAppSelector, useAppDispatch} from './store/hooks'
|
||||
|
||||
import {IUser} from './user'
|
||||
|
||||
const App = React.memo((): JSX.Element => {
|
||||
const language = useAppSelector<string>(getLanguage)
|
||||
const loggedIn = useAppSelector<boolean|null>(getLoggedIn)
|
||||
const globalError = useAppSelector<string>(getGlobalError)
|
||||
const me = useAppSelector<IUser|null>(getMe)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
useEffect(() => {
|
||||
@ -45,6 +50,12 @@ const App = React.memo((): JSX.Element => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (me) {
|
||||
TelemetryClient.setUser(me)
|
||||
}
|
||||
}, [me])
|
||||
|
||||
let globalErrorRedirect = null
|
||||
if (globalError) {
|
||||
globalErrorRedirect = <Route path='/*'><Redirect to={`/error?id=${globalError}`}/></Route>
|
||||
|
@ -19,6 +19,8 @@ import {updateView} from '../store/views'
|
||||
|
||||
import './centerPanel.scss'
|
||||
|
||||
import TelemetryClient from '../../../webapp/src/telemetry/telemetryClient'
|
||||
|
||||
import CardDialog from './cardDialog'
|
||||
import RootPortal from './rootPortal'
|
||||
import TopBar from './topBar'
|
||||
@ -80,6 +82,10 @@ class CenterPanel extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
TelemetryClient.trackEvent('boards', 'view', {viewType: this.props.activeView.fields.viewType})
|
||||
}
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
@ -92,6 +98,10 @@ class CenterPanel extends React.Component<Props, State> {
|
||||
return true
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
TelemetryClient.trackEvent('boards', 'view', {viewType: this.props.activeView.fields.viewType})
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const {groupByProperty, activeView, board, views, cards} = this.props
|
||||
const {visible: visibleGroups, hidden: hiddenGroups} = this.getVisibleAndHiddenGroups(cards, activeView.fields.visibleOptionIds, activeView.fields.hiddenOptionIds, groupByProperty)
|
||||
|
7
webapp/src/config/clientConfig.ts
Normal file
7
webapp/src/config/clientConfig.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type ClientConfig = {
|
||||
telemetry: boolean,
|
||||
telemetryid: string,
|
||||
}
|
@ -6,6 +6,7 @@ import {IWorkspace} from './blocks/workspace'
|
||||
import {OctoUtils} from './octoUtils'
|
||||
import {IUser} from './user'
|
||||
import {Utils} from './utils'
|
||||
import {ClientConfig} from './config/clientConfig'
|
||||
|
||||
//
|
||||
// OctoClient is the client interface to the server APIs
|
||||
@ -64,6 +65,20 @@ class OctoClient {
|
||||
localStorage.removeItem('focalboardSessionId')
|
||||
}
|
||||
|
||||
async getClientConfig(): Promise<ClientConfig | null> {
|
||||
const path = '/api/v1/clientConfig'
|
||||
const response = await fetch(this.serverUrl + path, {
|
||||
method: 'GET',
|
||||
headers: this.headers(),
|
||||
})
|
||||
if (response.status !== 200) {
|
||||
return null
|
||||
}
|
||||
|
||||
const json = (await this.getJson(response, {})) as ClientConfig
|
||||
return json
|
||||
}
|
||||
|
||||
async register(email: string, username: string, password: string, token?: string): Promise<{code: number, json: any}> {
|
||||
const path = '/api/v1/register'
|
||||
const body = JSON.stringify({email, username, password, token})
|
||||
|
7
webapp/src/telemetry/telemetry.ts
Normal file
7
webapp/src/telemetry/telemetry.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export interface TelemetryHandler {
|
||||
trackEvent: (userId: string, userRoles: string, category: string, event: string, props?: any) => void;
|
||||
pageVisited: (userId: string, userRoles: string, category: string, name: string) => void;
|
||||
}
|
24
webapp/src/telemetry/telemetryClient.test.ts
Normal file
24
webapp/src/telemetry/telemetryClient.test.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import TelemetryClient from './telemetryClient'
|
||||
|
||||
describe('trackEvent', () => {
|
||||
const track = jest.fn()
|
||||
const page = jest.fn()
|
||||
test('should call Rudder\'s track when a RudderTelemetryHandler is attached to TelemetryClient', () => {
|
||||
TelemetryClient.setTelemetryHandler()
|
||||
TelemetryClient.trackEvent('test', 'onClick')
|
||||
TelemetryClient.pageVisited('focalboard', 'test')
|
||||
expect(track).not.toHaveBeenCalled()
|
||||
expect(page).not.toHaveBeenCalled()
|
||||
|
||||
TelemetryClient.setTelemetryHandler({trackEvent: track, pageVisited: page})
|
||||
TelemetryClient.trackEvent('test', 'onClick')
|
||||
TelemetryClient.pageVisited('focalboard', 'test')
|
||||
|
||||
expect(track).toHaveBeenCalledTimes(1)
|
||||
expect(page).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
37
webapp/src/telemetry/telemetryClient.ts
Normal file
37
webapp/src/telemetry/telemetryClient.ts
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {IUser} from '../user'
|
||||
|
||||
import {TelemetryHandler} from './telemetry'
|
||||
|
||||
class TelemetryClient {
|
||||
public telemetryHandler?: TelemetryHandler
|
||||
public user?: IUser
|
||||
|
||||
setTelemetryHandler(telemetryHandler?: TelemetryHandler): void {
|
||||
this.telemetryHandler = telemetryHandler
|
||||
}
|
||||
|
||||
setUser(user: IUser): void {
|
||||
this.user = user
|
||||
}
|
||||
|
||||
trackEvent(category: string, event: string, props?: any): void {
|
||||
if (this.telemetryHandler) {
|
||||
const userId = this.user?.id
|
||||
this.telemetryHandler.trackEvent(userId || '', '', category, event, props)
|
||||
}
|
||||
}
|
||||
|
||||
pageVisited(category: string, name: string): void {
|
||||
if (this.telemetryHandler) {
|
||||
const userId = this.user?.id
|
||||
this.telemetryHandler.pageVisited(userId || '', '', category, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const telemetryClient = new TelemetryClient()
|
||||
|
||||
export {TelemetryClient}
|
||||
export default telemetryClient
|
Loading…
Reference in New Issue
Block a user