Skip to content

Commit

Permalink
feat(cli): send telemetry data from cli (#3067)
Browse files Browse the repository at this point in the history
* feat(cli): send telemetry data from cli

* chore(cli): remove debug code

* fix(cli): fix typo in description

* chore(cli): remove unused code

* chore(cli): remove unneseceary async

* feat(docs): add cli telemetry section

* chore(cli): add changeset

* fix(cli): get OS name from node-os package

* fix(docs): furtger readings link

* fix(docs): change dobule quote to backquote

* chore(docs): change mdx to md

* feat(docs): change cli telemetry description

Co-authored-by: Ömer Faruk APLAK <omer@pankod.com>
  • Loading branch information
alicanerdurmaz and omeraplak committed Nov 28, 2022
1 parent 94876eb commit 6f83ddb
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 51 deletions.
6 changes: 6 additions & 0 deletions .changeset/many-shoes-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pankod/refine-cli": minor
---

Added: `whoami` command to `refine-cli`. It's shows details of the development environment.
Added: telemetry to `refine-cli` commands.
54 changes: 43 additions & 11 deletions documentation/docs/further-readings/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,38 @@ title: Telemetry
sidebar_label: Telemetry
---

# Telemetry
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

# Telemetry

## Summary

**refine** implements a **simple** and **transparent** telemetry module for collecting usage statistics defined in a **very limited scope**.
**refine** implements a **simple** and **transparent** telemetry module for collecting usage statistics defined in a **very limited scope**.

Tracking is totally **secure** and **anonymous**. It includes no personally identifiable information and **does not use cookies**. Participation is optional and users may easily **opt-out**.


## Why do we need this?

We try to answer the question **how many users are actively using the Refine framework**. This information is critical for open-source projects like Refine to better understand their communities and measure their growth metrics.


## How do we collect data?

The tracking happens when a Refine application is loaded on the user's browser. On application init, a single HTTP request is sent to [https://telemetry.refine.dev](https://telemetry.refine.dev). The request body is encoded with Base64 to be decoded on Refine servers.
<Tabs>
<TabItem value="refine-core" label="refine core" default>
The tracking happens when a Refine application is loaded on the user's browser. On application init, a single HTTP request is sent to <a target="_blank" rel="noopener" href="https://telemetry.refine.dev">https://telemetry.refine.dev</a>. The request body is encoded with Base64 to be decoded on Refine servers.

There are no consequent requests for that session, as we do NOT collect any behavioral information such as _page views_, _button clicks_, etc.


## What is collected?

The HTTP call has a JSON payload having the following application-specific attributes:

| Value | Type | Description |
| ------------- | --------- | --------------------------------------------------------------------------------------------------------------- |
| providers | boolean[] | List of providers used in the project (auth, data, router, live, notification, auditLog, i18n or accessControl) |
| version | string | Version of the refine package. |
| resourceCount | number | Number of total resources. |
| Value | Type | Description |
| ------------- | --------- | --------------------------------------------------------------------------------------------------------------- |
| providers | `boolean[]` | List of providers used in the project (auth, data, router, live, notification, auditLog, i18n or accessControl) |
| version | `string` | Version of the refine package. |
| resourceCount | `number` | Number of total resources. |

Additionally, the following information is extracted and collected from the HTTP header:

Expand All @@ -48,3 +49,34 @@ Additionally, the following information is extracted and collected from the HTTP
## How to opt-out?

You can opt out of telemetry by simply adding `disableTelemetry` prop to the `<Refine />` component.

</TabItem>

<TabItem value="refine-cli" label="refine CLI">

After running a command with the `refine` CLI, a single HTTP request is sent to <a target="_blank" rel="noopener" href="https://telemetry.refine.dev/cli">https://telemetry.refine.dev/cli</a>.

## What is collected?

| Value | Type | Description |
| ---------------- | ------------------------------------------- | ------------------------------------------------------------ |
| nodeEnv | `string` | Specifies the environment in which an application is running. |
| nodeVersion | `string` | Installed Node.js version. |
| os | `string` | Operating system name. |
| osVersion | `string` | Operating system version. |
| command | `string` | Running script name. |
| packages | `{ "name": "string", "version": "string" }[]` | Installed `refine` packages. |
| projectFramework | `string` | Installed `react` framework. |

Additionally, the following information is extracted and collected from the HTTP header:

| Value | Description |
| ---------- | ----------------------------------------------------- |
| IP Address | IP Address of the machine the request is coming from. |

## How to opt-out?

You can opt out of telemetry by simply adding `REFINE_NO_TELEMETRY=true` to environment variables.

</TabItem>
</Tabs>
21 changes: 13 additions & 8 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,40 @@
},
"devDependencies": {
"@esbuild-plugins/node-resolve": "^0.1.4",
"@types/envinfo": "^7.8.1",
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^8.2.5",
"@types/jest": "^26.0.24",
"@types/temp": "^0.9.1",
"@types/jscodeshift": "^0.11.5",
"@types/node-fetch": "^2.6.2",
"@types/temp": "^0.9.1",
"jest": "^27.5.1",
"ts-jest": "^27.1.3",
"tsup": "^5.11.13",
"typescript": "^4.7.4",
"figlet": "^1.5.2"
},
"dependencies": {
"tslib": "^2.3.1",
"handlebars": "^4.7.7",
"fs-extra": "^10.1.0",
"temp": "^0.9.4",
"pluralize": "^8.0.0",
"chalk": "^4.1.2",
"commander": "9.4.1",
"conf": "^10.2.0",
"dotenv": "^16.0.3",
"envinfo": "^7.8.1",
"execa": "^5.1.1",
"fs-extra": "^10.1.0",
"handlebars": "^4.7.7",
"ink": "^3.2.0",
"ink-table": "^3.0.0",
"inquirer": "^8.2.5",
"jscodeshift": "^0.14.0",
"node-env-type": "^0.0.8",
"node-fetch": "^2.6.7",
"ora": "^5.4.1",
"pluralize": "^8.0.0",
"preferred-pm": "^3.0.3",
"jscodeshift": "^0.14.0",
"semver-diff": "^3.1.1"
"semver-diff": "^3.1.1",
"temp": "^0.9.4",
"tslib": "^2.3.1"
},
"author": "refine",
"license": "MIT",
Expand Down
37 changes: 37 additions & 0 deletions packages/cli/src/commands/whoami/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { getInstalledRefinePackages } from "@utils/package";
import { Command } from "commander";
import envinfo from "envinfo";
import ora from "ora";

const whoami = (program: Command) => {
return program
.command("whoami")
.description("View the details of the development environment")
.action(action);
};

const action = async () => {
const spinner = ora("Loading environment details...").start();
const info = await envinfo.run(
{
System: ["OS", "CPU"],
Binaries: ["Node", "Yarn", "npm"],
Browsers: ["Chrome", "Firefox", "Safari"],
},
{ showNotFound: true, markdown: true },
);

const packages = await getInstalledRefinePackages();
const packagesMarkdown = packages
.map((pkg) => {
return ` - ${pkg.name}: ${pkg.version}`;
})
.join("\n");

spinner.stop();
console.log(info);
console.log("## Refine Packages:");
console.log(packagesMarkdown);
};

export default whoami;
1 change: 1 addition & 0 deletions packages/cli/src/definitions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./projectTypes";
export * from "./uiFrameworks";
export * from "./package";
export * from "./node";
8 changes: 8 additions & 0 deletions packages/cli/src/definitions/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type NODE_ENV =
| "development"
| "production"
| "test"
| "continuous-integration"
| "system-integration-testing"
| "user-acceptance-testing"
| "custom";
10 changes: 10 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import figlet from "figlet";

import checkUpdates from "@commands/check-updates";
import createResource from "@commands/create-resource";
import whoami from "@commands/whoami";
import update from "@commands/update";
import { dev, build, start, run } from "@commands/runner";
import "@utils/env";
import { getPackageJson } from "@utils/package";
import { telemetryHook } from "@telemetryindex";

const bootstrap = () => {
let packageJson;
Expand Down Expand Up @@ -62,6 +64,14 @@ const bootstrap = () => {
build(program);
start(program);
run(program);
whoami(program);

program.hook("postAction", (thisCommand) => {
const command = thisCommand.args[0];
if (["run"].includes(command)) return;

telemetryHook();
});

program.parse(process.argv);

Expand Down
50 changes: 50 additions & 0 deletions packages/cli/src/telemetry/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { NODE_ENV } from "@definitions/node";
import { ProjectTypes } from "@definitions/projectTypes";
import { ENV, parseNodeEnv } from "@utils/env";
import { getOS } from "@utils/os";
import { getInstalledRefinePackages } from "@utils/package";
import { getProjectType } from "@utils/project";
import fetch from "node-fetch";

interface TelemetryData {
nodeEnv?: NODE_ENV;
nodeVersion: string;
os: string;
osVersion: string;
command: string;
packages: {
name: string;
version: string;
}[];
projectFramework: ProjectTypes;
}

export const getTelemetryData = async (): Promise<TelemetryData> => {
const os = await getOS();

const data = {
nodeEnv: parseNodeEnv(),
nodeVersion: process.version,
os: os.name,
osVersion: os.version,
command: process.argv[2],
packages: await getInstalledRefinePackages(),
projectFramework: getProjectType(),
};

return data;
};

export const telemetryHook = async () => {
if (ENV.REFINE_NO_TELEMETRY === "true") return;

try {
const data = await getTelemetryData();

fetch("https://telemetry.refine.dev/cli", {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
} catch (error) {}
};
14 changes: 14 additions & 0 deletions packages/cli/src/utils/env/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import { NODE_ENV } from "@definitions/node";
import { env } from "node-env-type";
import * as dotenv from "dotenv";
dotenv.config();

export const ENV = {
REFINE_NO_TELEMETRY: process.env.REFINE_NO_TELEMETRY || "false",
UPDATE_NOTIFIER_IS_DISABLED:
process.env.UPDATE_NOTIFIER_IS_DISABLED || "false",
UPDATE_NOTIFIER_CACHE_TTL:
process.env.UPDATE_NOTIFIER_CACHE_TTL || 1000 * 60 * 60 * 24, // 24 hours,
};

export const parseNodeEnv = (): NODE_ENV => {
if (env.isDev) return "development";
if (env.isProd) return "production";
if (env.isTest) return "test";
if (env.isCI) return "continuous-integration";
if (env.isUAT) return "user-acceptance-testing";
if (env.isSIT) return "system-integration-testing";

return "custom";
};
25 changes: 25 additions & 0 deletions packages/cli/src/utils/os/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import envinfo from "envinfo";
import os from "os";

export const getOSType = () => {
const osPlatform = os.type();

const types: Record<string, "macOS" | "Linux" | "Windows"> = {
Darwin: "macOS",
Linux: "Linux",
Windows_NT: "Windows",
};

return types[osPlatform];
};

export const getOS = async () => {
// returns as a ['OS', 'macOS Mojave 10.14.5']
const [_, OSInfo] =
(await envinfo.helpers.getOSInfo()) as unknown as string[];

return {
name: getOSType(),
version: OSInfo,
};
};
45 changes: 13 additions & 32 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
{
"include": [
"src"
],
"include": ["src"],
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"rootDir": "./src",
Expand All @@ -10,34 +8,17 @@
"importHelpers": false,
"strict": true,
"paths": {
"@definitions/*": [
"src/definitions/*"
],
"@definitions": [
"src/definitions"
],
"@utils/*": [
"src/utils/*"
],
"@utils": [
"src/utils"
],
"@commands/*": [
"src/commands/*"
],
"@commands": [
"src/commands"
],
"@components/*": [
"src/components/*"
],
"@components": [
"src/components"
]
"@definitions/*": ["src/definitions/*"],
"@definitions": ["src/definitions"],
"@utils/*": ["src/utils/*"],
"@utils": ["src/utils"],
"@commands/*": ["src/commands/*"],
"@commands": ["src/commands"],
"@components/*": ["src/components/*"],
"@components": ["src/components"],
"@telemetry*": ["src/telemetry/*"],
"@telemetry": ["src/telemetry"]
},
"typeRoots": [
"./src/types",
"../../node_modules/@types"
]
"typeRoots": ["./src/types", "../../node_modules/@types"]
}
}
}

0 comments on commit 6f83ddb

Please sign in to comment.