Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 30 additions & 31 deletions packages/core/src/lib/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
type Device,
type PackageName,
type Project,
type Telemetry,
type TelemetryVersion1,
type TelemetryVersion2and3,
} from '../types/telemetry'
import { type DatabaseProvider } from '../types'
import { type InitialisedList } from './core/initialise-lists'
Expand All @@ -30,17 +31,6 @@ const packageNames: PackageName[] = [
'@opensaas/keystone-nextjs-auth',
]

type TelemetryVersion1 =
| undefined
| false
| {
device: { lastSentDate?: string, informedAt: string }
projects: {
default: { lastSentDate?: string, informedAt: string }
[projectPath: string]: { lastSentDate?: string, informedAt: string }
}
}

function log (message: unknown) {
if (process.env.KEYSTONE_TELEMETRY_DEBUG === '1') {
console.log(`${message}`)
Expand All @@ -51,38 +41,46 @@ function getTelemetryConfig () {
const userConfig = new Conf<Configuration>({
projectName: 'keystonejs',
projectSuffix: '',
projectVersion: '2.0.0',
projectVersion: '3.0.0',
migrations: {
'^2.0.0': (store: Conf<Configuration>) => {
const existing = store.get('telemetry') as unknown as TelemetryVersion1
if (!existing) return
'^2.0.0': (store) => {
const existing = store.get('telemetry') as TelemetryVersion1
if (!existing) return // skip non-configured or known opt-outs

const replacement: Telemetry = {
// every informedAt was a copy of device.informedAt, it was copied everywhere
informedAt: existing.device.informedAt,
const replacement: TelemetryVersion2and3 = {
informedAt: null, // re-inform
Copy link
Member

@dcousens dcousens Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone has unknowingly missed their opt-out failing, this will re-inform them

device: {
lastSentDate: existing.device.lastSentDate ?? null,
},
projects: {}, // manually copying this below
projects: {}, // see below
}

// copy existing project lastSentDate's
for (const [projectPath, project] of Object.entries(existing.projects)) {
if (projectPath === 'default') continue // informedAt moved to root
if (projectPath === 'default') continue // informedAt moved to device.lastSentDate

// dont copy garbage
if (typeof project !== 'object') continue
if (typeof project.lastSentDate !== 'string') continue
if (new Date(project.lastSentDate).toString() === 'Invalid Date') continue

// only lastSentDate is retained
// retain lastSentDate
replacement.projects[projectPath] = {
lastSentDate: project.lastSentDate,
}
}

store.set('telemetry', replacement)
},
'^3.0.0': (store) => {
const existing = store.get('telemetry') as TelemetryVersion2and3
if (!existing) return // skip non-configured or known opt-outs

store.set('telemetry', {
...existing,
informedAt: null, // re-inform
Copy link
Member

@dcousens dcousens Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone has unknowingly missed their opt-out failing, this will re-inform them

} satisfies TelemetryVersion2and3)
},
},
})

Expand All @@ -102,8 +100,8 @@ function getDefaultedTelemetryConfig () {
device: {
lastSentDate: null,
},
projects: {} as Telemetry['projects'], // help Typescript infer the type
},
projects: {},
} as TelemetryVersion2and3, // help Typescript infer the type
userConfig,
}
}
Expand Down Expand Up @@ -189,8 +187,8 @@ export function printTelemetryStatus () {
function inform () {
const { telemetry, userConfig } = getDefaultedTelemetryConfig()

// no telemetry? somehow our earlier checks missed an opt out, do nothing
if (telemetry === false) return
// no telemetry? somehow our earlier missed something, do nothing
if (!telemetry) return

console.log() // gap to help visiblity
console.log(`${bold('Keystone Telemetry')}`)
Expand Down Expand Up @@ -225,8 +223,8 @@ async function sendProjectTelemetryEvent (
) {
const { telemetry, userConfig } = getDefaultedTelemetryConfig()

// no telemetry? somehow our earlier checks missed an opt out, do nothing
if (telemetry === false) return
// no telemetry? somehow our earlier missed something, do nothing
if (!telemetry) return

const project = telemetry.projects[cwd] ?? { lastSentDate: null }
const { lastSentDate } = project
Expand All @@ -251,8 +249,8 @@ async function sendProjectTelemetryEvent (
async function sendDeviceTelemetryEvent () {
const { telemetry, userConfig } = getDefaultedTelemetryConfig()

// no telemetry? somehow our earlier checks missed an opt out, do nothing
if (telemetry === false) return
// no telemetry? somehow our earlier missed something, do nothing
if (!telemetry) return

const { lastSentDate } = telemetry.device
if (lastSentDate && lastSentDate >= todaysDate) {
Expand Down Expand Up @@ -288,7 +286,8 @@ export async function runTelemetry (
const { telemetry } = getDefaultedTelemetryConfig()

// don't run if the user has opted out
if (telemetry === false) return
// or if somehow our defaults are problematic, do nothing
if (!telemetry) return

// don't send telemetry before we inform the user, allowing opt-out
if (!telemetry.informedAt) return inform()
Expand Down
36 changes: 25 additions & 11 deletions packages/core/src/types/telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
import type { DatabaseProvider } from './core'

export type Telemetry = {
informedAt: string | null
device: {
lastSentDate: string | null
}
projects: Partial<{
[projectPath: string]: {
lastSentDate: string
export type TelemetryVersion1 =
| undefined
| false
| {
device: { lastSentDate?: string, informedAt: string }
projects: {
default: { lastSentDate?: string, informedAt: string }
[projectPath: string]: { lastSentDate?: string, informedAt: string }
}
}
}>
}

export type TelemetryVersion2and3 =
| undefined
| false
| {
informedAt: string | null
device: {
lastSentDate: string | null
}
projects: Partial<{
[projectPath: string]: {
lastSentDate: string
}
}>
}

export type Configuration = {
telemetry?: undefined | false | Telemetry
telemetry?: undefined | false | TelemetryVersion2and3
}

export type Device = {
Expand Down