mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
c9bba5207e
Summary: Adds new telemetry events and a flag for whether an event originated from a team site. Test Plan: Manual. Reviewers: paulfitz Reviewed By: paulfitz Subscribers: jarek, dsagal Differential Revision: https://phab.getgrist.com/D4105
1584 lines
49 KiB
TypeScript
1584 lines
49 KiB
TypeScript
import {StringUnion} from 'app/common/StringUnion';
|
|
|
|
/**
|
|
* Telemetry levels, in increasing order of data collected.
|
|
*/
|
|
export enum Level {
|
|
off = 0,
|
|
limited = 1,
|
|
full = 2,
|
|
}
|
|
|
|
/**
|
|
* A set of contracts that all telemetry events must follow prior to being
|
|
* logged.
|
|
*
|
|
* Currently, this includes meeting minimum telemetry levels for events
|
|
* and their metadata, and passing in the correct data type for the value of
|
|
* each metadata property.
|
|
*
|
|
* The `minimumTelemetryLevel` defined at the event level will also be applied
|
|
* to all metadata properties of an event, and can be overridden at the metadata
|
|
* level.
|
|
*/
|
|
export const TelemetryContracts: TelemetryContracts = {
|
|
apiUsage: {
|
|
description: 'Triggered when an HTTP request with an API key is made.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
method: {
|
|
description: 'The HTTP request method (e.g. GET, POST, PUT).',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
userAgent: {
|
|
description: 'The User-Agent HTTP request header.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
assistantOpen: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when the AI Assistant is first opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'short',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
context: {
|
|
description: 'The type of assistant (e.g. "formula"), table id, and column id.',
|
|
dataType: 'object',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
assistantSend: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when a message is sent to the AI Assistant.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'short',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
siteId: {
|
|
description: 'The id of the site.',
|
|
dataType: 'number',
|
|
},
|
|
siteType: {
|
|
description: 'The type of the site.',
|
|
dataType: 'string',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
access: {
|
|
description: 'The document access level of the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
context: {
|
|
description: 'The type of assistant (e.g. "formula"), table id, and column id.',
|
|
dataType: 'object',
|
|
},
|
|
prompt: {
|
|
description: 'The role ("user" or "system"), content, and index of the message sent to the AI Assistant.',
|
|
dataType: 'object',
|
|
},
|
|
},
|
|
},
|
|
assistantReceive: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when a message is received from the AI Assistant.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'short',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
siteId: {
|
|
description: 'The id of the site.',
|
|
dataType: 'number',
|
|
},
|
|
siteType: {
|
|
description: 'The type of the site.',
|
|
dataType: 'string',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
access: {
|
|
description: 'The document access level of the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
context: {
|
|
description: 'The type of assistant (e.g. "formula"), table id, and column id.',
|
|
dataType: 'object',
|
|
},
|
|
message: {
|
|
description: 'The content and index of the message received from the AI Assistant.',
|
|
dataType: 'object',
|
|
},
|
|
suggestedFormula: {
|
|
description: 'The formula suggested by the AI Assistant, if present.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
assistantSave: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when changes in the expanded formula editor are saved after the AI Assistant ' +
|
|
'was opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'short',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
context: {
|
|
description: 'The type of assistant (e.g. "formula"), table id, and column id.',
|
|
dataType: 'object',
|
|
},
|
|
newFormula: {
|
|
description: 'The formula that was saved.',
|
|
dataType: 'string',
|
|
},
|
|
oldFormula: {
|
|
description: 'The formula that was overwritten.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
assistantCancel: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when changes in the expanded formula editor are discarded after the AI Assistant ' +
|
|
'was opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'short',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
conversationLength: {
|
|
description: 'The number of messages sent and received since opening the AI Assistant.',
|
|
dataType: 'number',
|
|
},
|
|
context: {
|
|
description: 'The type of assistant (e.g. "formula"), table id, and column id.',
|
|
dataType: 'object',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
assistantApplySuggestion: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when a suggested formula from one of the received messages was applied and saved.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
conversationLength: {
|
|
description: 'The number of messages sent and received since opening the AI Assistant.',
|
|
dataType: 'number',
|
|
},
|
|
conversationHistoryLength: {
|
|
description: "The number of messages in the conversation's history. May be less than conversationLength "
|
|
+ "if the conversation history was cleared in the same session.",
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
assistantClearConversation: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when a conversation in the AI Assistant is cleared.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'short',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
context: {
|
|
description: 'The type of assistant (e.g. "formula"), table id, and column id.',
|
|
dataType: 'object',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
assistantClose: {
|
|
category: 'AIAssistant',
|
|
description: 'Triggered when a formula is saved or discarded after the AI Assistant was opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
conversationId: {
|
|
description: 'A random identifier for the current conversation with the assistant.',
|
|
dataType: 'string',
|
|
},
|
|
suggestionApplied: {
|
|
description: 'True if a suggested formula from one of the received messages was applied.',
|
|
dataType: 'boolean',
|
|
},
|
|
conversationLength: {
|
|
description: 'The number of messages sent and received since opening the AI Assistant.',
|
|
dataType: 'number',
|
|
},
|
|
conversationHistoryLength: {
|
|
description: "The number of messages in the conversation's history. May be less than conversationLength "
|
|
+ "if the conversation history was cleared in the same session.",
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
beaconOpen: {
|
|
category: 'HelpCenter',
|
|
description: 'Triggered when HelpScout Beacon is opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
beaconArticleViewed: {
|
|
category: 'HelpCenter',
|
|
description: 'Triggered when an article is opened in HelpScout Beacon.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
articleId: {
|
|
description: 'The id of the article.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
beaconEmailSent: {
|
|
category: 'HelpCenter',
|
|
description: 'Triggered when an email is sent in HelpScout Beacon.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
beaconSearch: {
|
|
category: 'HelpCenter',
|
|
description: 'Triggered when a search is made in HelpScout Beacon.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
searchQuery: {
|
|
description: 'The search query.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
documentCreated: {
|
|
description: 'Triggered when a document is created.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the id of the created document.',
|
|
dataType: 'string',
|
|
},
|
|
sourceDocIdDigest: {
|
|
description: 'A hash of the id of the source document, if the document was '
|
|
+ 'duplicated from an existing document.',
|
|
dataType: 'string',
|
|
},
|
|
isImport: {
|
|
description: 'Whether the document was created by import.',
|
|
dataType: 'boolean',
|
|
},
|
|
isSaved: {
|
|
description: 'Whether the document was saved to a workspace.',
|
|
dataType: 'boolean',
|
|
},
|
|
fileType: {
|
|
description: 'If the document was created by import, the file extension '
|
|
+ 'of the file that was imported.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
},
|
|
},
|
|
documentForked: {
|
|
description: 'Triggered when a document is forked.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
siteId: {
|
|
description: 'The id of the site containing the forked document.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
siteType: {
|
|
description: 'The type of the site.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
access: {
|
|
description: 'The document access level of the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
forkIdDigest: {
|
|
description: 'A hash of the fork id.',
|
|
dataType: 'string',
|
|
},
|
|
forkDocIdDigest: {
|
|
description: 'A hash of the full id of the fork, including the trunk id and fork id.',
|
|
dataType: 'string',
|
|
},
|
|
trunkIdDigest: {
|
|
description: 'A hash of the trunk id.',
|
|
dataType: 'string',
|
|
},
|
|
isTemplate: {
|
|
description: 'Whether the trunk is a template.',
|
|
dataType: 'boolean',
|
|
},
|
|
lastActivity: {
|
|
description: 'Timestamp of the last update to the trunk document.',
|
|
dataType: 'date',
|
|
},
|
|
},
|
|
},
|
|
documentOpened: {
|
|
description: 'Triggered when a public document or template is opened.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
siteId: {
|
|
description: 'The site id.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
siteType: {
|
|
description: 'The site type.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
access: {
|
|
description: 'The document access level of the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
isPublic: {
|
|
description: 'Whether the document is public.',
|
|
dataType: 'boolean',
|
|
},
|
|
isSnapshot: {
|
|
description: 'Whether a snapshot was opened.',
|
|
dataType: 'boolean',
|
|
},
|
|
isTemplate: {
|
|
description: 'Whether the document is a template.',
|
|
dataType: 'boolean',
|
|
},
|
|
lastUpdated: {
|
|
description: 'Timestamp of when the document was last updated.',
|
|
dataType: 'date',
|
|
},
|
|
},
|
|
},
|
|
documentUsage: {
|
|
description: 'Triggered on doc open and close, as well as hourly while a document is open.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
siteId: {
|
|
description: 'The site id.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
siteType: {
|
|
description: 'The site type.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
access: {
|
|
description: 'The document access level of the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
triggeredBy: {
|
|
description: 'What caused this event to trigger. May be either "docOpen", "interval", or "docClose".',
|
|
dataType: 'string',
|
|
},
|
|
isPublic: {
|
|
description: 'Whether the document is public.',
|
|
dataType: 'boolean',
|
|
},
|
|
rowCount: {
|
|
description: 'The number of rows in the document.',
|
|
dataType: 'number',
|
|
},
|
|
dataSizeBytes: {
|
|
description: 'The total size of all data in the document, excluding attachments.',
|
|
dataType: 'number',
|
|
},
|
|
attachmentsSize: {
|
|
description: 'The total size of all attachments in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numAccessRules: {
|
|
description: 'The number of access rules in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numUserAttributes: {
|
|
description: 'The number of user attributes in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numAttachments: {
|
|
description: 'The number of attachments in the document.',
|
|
dataType: 'number',
|
|
},
|
|
attachmentTypes: {
|
|
description: "A list of unique file extensions compiled from all of the document's attachments.",
|
|
dataType: 'string[]',
|
|
},
|
|
numCharts: {
|
|
description: 'The number of charts in the document.',
|
|
dataType: 'number',
|
|
},
|
|
chartTypes: {
|
|
description: 'A list of chart types of every chart in the document.',
|
|
dataType: 'string[]',
|
|
},
|
|
numLinkedCharts: {
|
|
description: 'The number of linked charts in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numLinkedWidgets: {
|
|
description: 'The number of linked widgets in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numColumns: {
|
|
description: 'The number of columns in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numColumnsWithConditionalFormatting: {
|
|
description: 'The number of columns with conditional formatting in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numFormulaColumns: {
|
|
description: 'The number of formula columns in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numTriggerFormulaColumns: {
|
|
description: 'The number of trigger formula columns in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numSummaryFormulaColumns: {
|
|
description: 'The number of summary formula columns in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numFieldsWithConditionalFormatting: {
|
|
description: 'The number of fields with conditional formatting in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numTables: {
|
|
description: 'The number of tables in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numOnDemandTables: {
|
|
description: 'The number of on-demand tables in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numTablesWithConditionalFormatting: {
|
|
description: 'The number of tables with conditional formatting in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numSummaryTables: {
|
|
description: 'The number of summary tables in the document.',
|
|
dataType: 'number',
|
|
},
|
|
numCustomWidgets: {
|
|
description: 'The number of custom widgets in the document.',
|
|
dataType: 'number',
|
|
},
|
|
customWidgetIds: {
|
|
description: 'A list of plugin ids for every custom widget in the document. '
|
|
+ 'The ids of widgets not created by Grist Labs are replaced with "externalId".',
|
|
dataType: 'string[]',
|
|
},
|
|
},
|
|
},
|
|
processMonitor: {
|
|
description: 'Triggered every 5 seconds.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
heapUsedMB: {
|
|
description: 'Size of JS heap in use, in MiB.',
|
|
dataType: 'number',
|
|
},
|
|
heapTotalMB: {
|
|
description: 'Total heap size, in MiB, allocated for JS by V8. ',
|
|
dataType: 'number',
|
|
},
|
|
cpuAverage: {
|
|
description: 'Fraction (typically between 0 and 1) of CPU usage. Includes all threads, so may exceed 1.',
|
|
dataType: 'number',
|
|
},
|
|
intervalMs: {
|
|
description: 'Interval (in milliseconds) over which `cpuAverage` is reported.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
sendingWebhooks: {
|
|
description: 'Triggered when sending webhooks.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
numEvents: {
|
|
description: 'The number of events in the batch of webhooks being sent.',
|
|
dataType: 'number',
|
|
},
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
siteId: {
|
|
description: 'The site id.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
siteType: {
|
|
description: 'The site type.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
access: {
|
|
description: 'The document access level of the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
},
|
|
},
|
|
signupFirstVisit: {
|
|
category: 'ProductVisits',
|
|
description: 'Triggered when a new user first opens the Grist app',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
siteId: {
|
|
description: 'The site id of first visit after signup.',
|
|
dataType: 'number',
|
|
},
|
|
siteType: {
|
|
description: 'The site type of first visit after signup.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that signed up.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
signupVerified: {
|
|
description: 'Triggered after a user successfully verifies their account during sign-up. '
|
|
+ 'Not triggered in grist-core.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
isAnonymousTemplateSignup: {
|
|
description: 'Whether the user viewed any templates before signing up.',
|
|
dataType: 'boolean',
|
|
},
|
|
templateId: {
|
|
description: 'The doc id of the template the user last viewed before signing up, if any.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
siteMembership: {
|
|
description: 'Triggered daily.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
siteId: {
|
|
description: 'The site id.',
|
|
dataType: 'number',
|
|
},
|
|
siteType: {
|
|
description: 'The site type.',
|
|
dataType: 'string',
|
|
},
|
|
numOwners: {
|
|
description: 'The number of users with an owner role in this site.',
|
|
dataType: 'number',
|
|
},
|
|
numEditors: {
|
|
description: 'The number of users with an editor role in this site.',
|
|
dataType: 'number',
|
|
},
|
|
numViewers: {
|
|
description: 'The number of users with a viewer role in this site.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
siteUsage: {
|
|
description: 'Triggered daily.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
siteId: {
|
|
description: 'The site id.',
|
|
dataType: 'number',
|
|
},
|
|
siteType: {
|
|
description: 'The site type.',
|
|
dataType: 'string',
|
|
},
|
|
inGoodStanding: {
|
|
description: "Whether the site's subscription is in good standing.",
|
|
dataType: 'boolean',
|
|
},
|
|
stripePlanId: {
|
|
description: 'The Stripe Plan id associated with this site.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
numDocs: {
|
|
description: 'The number of docs in this site.',
|
|
dataType: 'number',
|
|
},
|
|
numWorkspaces: {
|
|
description: 'The number of workspaces in this site.',
|
|
dataType: 'number',
|
|
},
|
|
numMembers: {
|
|
description: 'The number of site members.',
|
|
dataType: 'number',
|
|
},
|
|
lastActivity: {
|
|
description: 'A timestamp of the most recent update made to a site document.',
|
|
dataType: 'date',
|
|
},
|
|
earliestDocCreatedAt: {
|
|
description: 'A timestamp of the earliest non-deleted document creation time.',
|
|
dataType: 'date',
|
|
},
|
|
},
|
|
},
|
|
tutorialOpened: {
|
|
category: 'Tutorial',
|
|
description: 'Triggered when a tutorial is opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
tutorialForkIdDigest: {
|
|
description: 'A hash of the tutorial fork id.',
|
|
dataType: 'string',
|
|
},
|
|
tutorialTrunkIdDigest: {
|
|
description: 'A hash of the tutorial trunk id.',
|
|
dataType: 'string',
|
|
},
|
|
lastSlideIndex: {
|
|
description: 'The 0-based index of the last tutorial slide the user had open.',
|
|
dataType: 'number',
|
|
},
|
|
numSlides: {
|
|
description: 'The total number of slides in the tutorial.',
|
|
dataType: 'number',
|
|
},
|
|
percentComplete: {
|
|
description: 'Percentage of tutorial completion.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
tutorialProgressChanged: {
|
|
category: 'Tutorial',
|
|
description: 'Triggered on changes to tutorial progress.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
tutorialForkIdDigest: {
|
|
description: 'A hash of the tutorial fork id.',
|
|
dataType: 'string',
|
|
},
|
|
tutorialTrunkIdDigest: {
|
|
description: 'A hash of the tutorial trunk id.',
|
|
dataType: 'string',
|
|
},
|
|
lastSlideIndex: {
|
|
description: 'The 0-based index of the last tutorial slide the user had open.',
|
|
dataType: 'number',
|
|
},
|
|
numSlides: {
|
|
description: 'The total number of slides in the tutorial.',
|
|
dataType: 'number',
|
|
},
|
|
percentComplete: {
|
|
description: 'Percentage of tutorial completion.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
tutorialRestarted: {
|
|
category: 'Tutorial',
|
|
description: 'Triggered when a tutorial is restarted.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
tutorialForkIdDigest: {
|
|
description: 'A hash of the tutorial fork id.',
|
|
dataType: 'string',
|
|
},
|
|
tutorialTrunkIdDigest: {
|
|
description: 'A hash of the tutorial trunk id.',
|
|
dataType: 'string',
|
|
},
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
siteId: {
|
|
description: 'The site id.',
|
|
dataType: 'number',
|
|
},
|
|
siteType: {
|
|
description: 'The site type.',
|
|
dataType: 'string',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
access: {
|
|
description: 'The document access level of the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
watchedVideoTour: {
|
|
category: 'Welcome',
|
|
description: 'Triggered when the video tour is closed.',
|
|
minimumTelemetryLevel: Level.limited,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
watchTimeSeconds: {
|
|
description: 'The number of seconds elapsed in the video player.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
minimumTelemetryLevel: Level.full,
|
|
},
|
|
},
|
|
},
|
|
answeredUseCaseQuestion: {
|
|
category: 'Welcome',
|
|
description: 'Triggered for each selected use case in the welcome questionnaire.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
useCase: {
|
|
description: 'The selected use case. If "Other", the response is also included.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
clickedScheduleCoachingCall: {
|
|
category: 'Welcome',
|
|
description: 'Triggered when the link to schedule a coaching call is clicked.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
deletedAccount: {
|
|
category: 'SubscriptionPlan',
|
|
description: 'Triggered when an account is deleted.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
},
|
|
createdSite: {
|
|
category: 'TeamSite',
|
|
description: 'Triggered when a site is created.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
siteId: {
|
|
description: 'The id of the site.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
deletedSite: {
|
|
category: 'TeamSite',
|
|
description: 'Triggered when a site is deleted.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
siteId: {
|
|
description: 'The id of the site.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
invitedMember: {
|
|
category: 'TeamSite',
|
|
description: 'Triggered when users are added to a team site.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
count: {
|
|
description: 'The number of users added.',
|
|
dataType: 'number',
|
|
},
|
|
siteId: {
|
|
description: 'The id of the site.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
uninvitedMember: {
|
|
category: 'TeamSite',
|
|
description: 'Triggered when users are removed from a team site.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
count: {
|
|
description: 'The number of users removed.',
|
|
dataType: 'number',
|
|
},
|
|
siteId: {
|
|
description: 'The id of the site.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
invitedDocUser: {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when users are added to a document.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
access: {
|
|
description: 'The access level granted to the added users.',
|
|
dataType: 'string',
|
|
},
|
|
count: {
|
|
description: 'The number of users added.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
madeDocPublic: {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when public access to a document is enabled.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
access: {
|
|
description: 'The access level granted to public users.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
madeDocPrivate: {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when public access to a document is disabled.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
openedTemplate: {
|
|
category: 'TemplateUsage',
|
|
description: 'Triggered when a template is opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
templateId: {
|
|
description: 'The document id of the template.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
openedTemplateTour: {
|
|
category: 'TemplateUsage',
|
|
description: 'Triggered when a document tour for a template is opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
templateId: {
|
|
description: 'The document id of the template.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
copiedTemplate: {
|
|
category: 'TemplateUsage',
|
|
description: 'Triggered when a copy of a template is saved.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
templateId: {
|
|
description: 'The document id of the template.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
subscribedToPlan: {
|
|
category: 'SubscriptionPlan',
|
|
description: 'Triggered on subscription to a plan.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
planName: {
|
|
description: 'The name of the plan.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
cancelledPlan: {
|
|
category: 'SubscriptionPlan',
|
|
description: 'Triggered on cancellation of a plan.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
planName: {
|
|
description: 'The name of the plan.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
createdWorkspace: {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when a workspace is created.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
workspaceId: {
|
|
description: 'The id of the workspace.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
deletedWorkspace: {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when a workspace is deleted.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
workspaceId: {
|
|
description: 'The id of the workspace.',
|
|
dataType: 'number',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
},
|
|
},
|
|
visitedPage: {
|
|
category: 'ProductVisits',
|
|
description: 'Triggered when a page is loaded.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id. Only included on visits to doc pages.',
|
|
dataType: 'string',
|
|
},
|
|
url: {
|
|
description: 'The URL of the visited page. Link keys, doc ids, and other identifiers ' +
|
|
'are excluded from the URL.',
|
|
dataType: 'string',
|
|
},
|
|
path: {
|
|
description: 'The path of the visited page (e.g. "app.html").',
|
|
dataType: 'string',
|
|
},
|
|
userAgent: {
|
|
description: 'The User-Agent HTTP request header.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
openedDoc: {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when a document is opened.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
'createdDoc-Empty': {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when a new empty document is created.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
'createdDoc-FileImport': {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when a document is created via file import.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
'createdDoc-CopyTemplate': {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when a document is created by saving a copy of a template.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
'createdDoc-CopyDoc': {
|
|
category: 'DocumentUsage',
|
|
description: 'Triggered when a document is created by saving a copy of a document.',
|
|
minimumTelemetryLevel: Level.full,
|
|
retentionPeriod: 'indefinitely',
|
|
metadataContracts: {
|
|
docIdDigest: {
|
|
description: 'A hash of the doc id.',
|
|
dataType: 'string',
|
|
},
|
|
userId: {
|
|
description: 'The id of the user that triggered this event.',
|
|
dataType: 'number',
|
|
},
|
|
altSessionId: {
|
|
description: 'A random, session-based identifier for the user that triggered this event.',
|
|
dataType: 'string',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
type TelemetryContracts = Record<TelemetryEvent, TelemetryEventContract>;
|
|
|
|
export const TelemetryEvents = StringUnion(
|
|
'apiUsage',
|
|
'assistantOpen',
|
|
'assistantSend',
|
|
'assistantReceive',
|
|
'assistantSave',
|
|
'assistantCancel',
|
|
'assistantApplySuggestion',
|
|
'assistantClearConversation',
|
|
'assistantClose',
|
|
'beaconOpen',
|
|
'beaconArticleViewed',
|
|
'beaconEmailSent',
|
|
'beaconSearch',
|
|
'documentCreated',
|
|
'documentForked',
|
|
'documentOpened',
|
|
'documentUsage',
|
|
'processMonitor',
|
|
'sendingWebhooks',
|
|
'signupFirstVisit',
|
|
'signupVerified',
|
|
'siteMembership',
|
|
'siteUsage',
|
|
'tutorialOpened',
|
|
'tutorialProgressChanged',
|
|
'tutorialRestarted',
|
|
'watchedVideoTour',
|
|
'answeredUseCaseQuestion',
|
|
'clickedScheduleCoachingCall',
|
|
'deletedAccount',
|
|
'createdSite',
|
|
'deletedSite',
|
|
'invitedMember',
|
|
'uninvitedMember',
|
|
'invitedDocUser',
|
|
'madeDocPublic',
|
|
'madeDocPrivate',
|
|
'openedTemplate',
|
|
'openedTemplateTour',
|
|
'copiedTemplate',
|
|
'subscribedToPlan',
|
|
'cancelledPlan',
|
|
'createdWorkspace',
|
|
'deletedWorkspace',
|
|
'visitedPage',
|
|
'openedDoc',
|
|
'createdDoc-Empty',
|
|
'createdDoc-FileImport',
|
|
'createdDoc-CopyTemplate',
|
|
'createdDoc-CopyDoc',
|
|
);
|
|
export type TelemetryEvent = typeof TelemetryEvents.type;
|
|
|
|
type TelemetryEventCategory =
|
|
| 'AIAssistant'
|
|
| 'HelpCenter'
|
|
| 'TemplateUsage'
|
|
| 'Tutorial'
|
|
| 'Welcome'
|
|
| 'SubscriptionPlan'
|
|
| 'DocumentUsage'
|
|
| 'TeamSite'
|
|
| 'ProductVisits';
|
|
|
|
interface TelemetryEventContract {
|
|
description: string;
|
|
minimumTelemetryLevel: Level;
|
|
retentionPeriod: TelemetryRetentionPeriod;
|
|
category?: TelemetryEventCategory;
|
|
metadataContracts?: Record<string, MetadataContract>;
|
|
}
|
|
|
|
export type TelemetryRetentionPeriod = 'short' | 'indefinitely';
|
|
|
|
interface MetadataContract {
|
|
description: string;
|
|
dataType: 'boolean' | 'number' | 'string' | 'string[]' | 'date' | 'object';
|
|
minimumTelemetryLevel?: Level;
|
|
}
|
|
|
|
export type TelemetryMetadataByLevel = Partial<Record<EnabledTelemetryLevel, TelemetryMetadata>>;
|
|
|
|
export type EnabledTelemetryLevel = Exclude<TelemetryLevel, 'off'>;
|
|
|
|
export const TelemetryLevels = StringUnion('off', 'limited', 'full');
|
|
export type TelemetryLevel = typeof TelemetryLevels.type;
|
|
|
|
export type TelemetryMetadata = Record<string, any>;
|
|
|
|
/**
|
|
* The name of a cookie that's set whenever a template is opened.
|
|
*
|
|
* The cookie remembers the last template that was opened, which is then read during
|
|
* sign-up to track which templates were viewed before sign-up.
|
|
*/
|
|
export const TELEMETRY_TEMPLATE_SIGNUP_COOKIE_NAME = 'gr_template_signup_trk';
|
|
|
|
/**
|
|
* Returns a function that accepts a telemetry event and metadata, and performs various
|
|
* checks on it based on a set of contracts and the `telemetryLevel`.
|
|
*
|
|
* The function throws if any checks fail.
|
|
*/
|
|
export function buildTelemetryEventChecker(telemetryLevel: TelemetryLevel) {
|
|
const currentTelemetryLevel = Level[telemetryLevel];
|
|
|
|
return (event: TelemetryEvent, metadata?: TelemetryMetadata) => {
|
|
const eventContract = TelemetryContracts[event];
|
|
if (!eventContract) {
|
|
throw new Error(`Unknown telemetry event: ${event}`);
|
|
}
|
|
|
|
const eventMinimumTelemetryLevel = eventContract.minimumTelemetryLevel;
|
|
if (currentTelemetryLevel < eventMinimumTelemetryLevel) {
|
|
throw new Error(
|
|
`Telemetry event ${event} requires a minimum telemetry level of ${eventMinimumTelemetryLevel} ` +
|
|
`but the current level is ${currentTelemetryLevel}`
|
|
);
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(metadata ?? {})) {
|
|
const metadataContract = eventContract.metadataContracts?.[key];
|
|
if (!metadataContract) {
|
|
throw new Error(`Unknown metadata for telemetry event ${event}: ${key}`);
|
|
}
|
|
|
|
const metadataMinimumTelemetryLevel = metadataContract.minimumTelemetryLevel;
|
|
if (metadataMinimumTelemetryLevel && currentTelemetryLevel < metadataMinimumTelemetryLevel) {
|
|
throw new Error(
|
|
`Telemetry metadata ${key} of event ${event} requires a minimum telemetry level of ` +
|
|
`${metadataMinimumTelemetryLevel} but the current level is ${currentTelemetryLevel}`
|
|
);
|
|
}
|
|
|
|
const {dataType} = metadataContract;
|
|
if (dataType.endsWith('[]')) {
|
|
if (!Array.isArray(value)) {
|
|
throw new Error(
|
|
`Telemetry metadata ${key} of event ${event} expected a value of type array ` +
|
|
`but received a value of type ${typeof value}`
|
|
);
|
|
}
|
|
|
|
const elementDataType = dataType.slice(0, -2);
|
|
if (value.some(element => typeof element !== elementDataType)) {
|
|
throw new Error(
|
|
`Telemetry metadata ${key} of event ${event} expected a value of type ${elementDataType}[] ` +
|
|
`but received a value of type ${typeof value}[]`
|
|
);
|
|
}
|
|
} else if (dataType === 'date') {
|
|
if (!(value instanceof Date) && typeof value !== 'string') {
|
|
throw new Error(
|
|
`Telemetry metadata ${key} of event ${event} expected a value of type Date or string ` +
|
|
`but received a value of type ${typeof value}`
|
|
);
|
|
}
|
|
if (typeof value === 'string' && !hasTimezone(value)) {
|
|
throw new Error(
|
|
`Telemetry metadata ${key} of event ${event} has an ambiguous date string`
|
|
);
|
|
}
|
|
} else if (dataType !== typeof value) {
|
|
throw new Error(
|
|
`Telemetry metadata ${key} of event ${event} expected a value of type ${dataType} ` +
|
|
`but received a value of type ${typeof value}`
|
|
);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Check that datetime looks like it has a timezone in it. If not,
|
|
// that could be a problem for whatever ingests the data.
|
|
function hasTimezone(isoDateString: string) {
|
|
// Use a regular expression to check for a timezone offset or 'Z'
|
|
return /([+-]\d{2}:\d{2}|Z)$/.test(isoDateString);
|
|
}
|
|
|
|
export type TelemetryEventChecker = (event: TelemetryEvent, metadata?: TelemetryMetadata) => void;
|