Skip to content

Commit

Permalink
fix: address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
marco-ippolito committed Nov 23, 2024
1 parent 93d867d commit 4a88385
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 31 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@ hdcli tracker run

```bash
# Send information about your project manifest files
hdcli report ingestion
hdcli report generate
```

It is possible to change the server to which the report is sent by setting the `NES_REPORT_URL` environment variable.

```bash
# Send information about your project manifest files
NES_REPORT_URL="https://example.com/graphql" hdcli report generate
```

## Tutorials
Expand Down
37 changes: 25 additions & 12 deletions libs/report/ingestion/src/lib/ingestion-manifest.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import { readFileSync, statSync } from 'node:fs';
import { readFileSync, statSync, existsSync } from 'node:fs';
import { promptToProceedUploadFile } from './prompts';
import { findManifestFile } from './send-manifest';

jest.mock('node:fs', () => ({
readFileSync: jest.fn(),
statSync: jest.fn(),
existsSync: jest.fn(),
}));

jest.mock('./prompts', () => ({
promptToProceedUploadFile: jest.fn(),
}));

describe('Telemetry Functions', () => {
it('should find manifest files correctly', async () => {
it('should find manifest files successfully', async () => {
const mockFileName = 'package.json';
const mockFileData = '{"name": "test package"}';
const mockFileStat = { size: 1024 };

(existsSync as jest.Mock).mockReturnValue(true);
(statSync as jest.Mock).mockReturnValue(mockFileStat);
(readFileSync as jest.Mock).mockReturnValue(mockFileData);
(promptToProceedUploadFile as jest.Mock).mockResolvedValue(true);
Expand All @@ -27,42 +28,54 @@ describe('Telemetry Functions', () => {
expect(promptToProceedUploadFile).toHaveBeenCalledWith(mockFileName);
});

it('manifest files does not exist', async () => {
(existsSync as jest.Mock).mockReturnValue(false);
(promptToProceedUploadFile as jest.Mock).mockResolvedValue(true);
const consoleWarnSpy = jest.spyOn(console, 'log').mockImplementation();
const result = await findManifestFile();

expect(result).toBeUndefined();
expect(consoleWarnSpy).toHaveBeenCalledWith('package.json not found');
consoleWarnSpy.mockRestore();
});

it('should warn if manifest file is empty', async () => {
const mockFileName = 'package.json';
const mockFileStat = { size: 0 };
(existsSync as jest.Mock).mockReturnValue(true);
(statSync as jest.Mock).mockReturnValue(mockFileStat);
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
const result = await findManifestFile();

expect(result).toBeUndefined();
expect(consoleWarnSpy).toHaveBeenCalledWith(`File ${mockFileName} is empty`);
expect(consoleWarnSpy).toHaveBeenCalledWith(
`[WARN] Ignoring file ${mockFileName}, because is empty`
);
consoleWarnSpy.mockRestore();
});

it('should warn if manifest file is too large', async () => {
const mockFileStat = { size: 6e6 }; // 6MB file, larger than the 5MB max size
const mockFileStat = { size: 6e6 }; // 6MB file, larger than the 1MB max size
(existsSync as jest.Mock).mockReturnValue(true);
(statSync as jest.Mock).mockReturnValue(mockFileStat);
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
const result = await findManifestFile();
const mockFileName = 'package.json';
expect(result).toBeUndefined();
expect(consoleWarnSpy).toHaveBeenCalledWith(`File ${mockFileName} is too large`);
expect(consoleWarnSpy).toHaveBeenCalledWith(
`[WARN] Ignoring file ${mockFileName}, because it is too large`
);
consoleWarnSpy.mockRestore();
});

it('should not proceed with upload if user rejects', async () => {
const mockFileName = 'package.json';
const mockFileStat = { size: 1024 };
(existsSync as jest.Mock).mockReturnValue(true);
(statSync as jest.Mock).mockReturnValue(mockFileStat);
(promptToProceedUploadFile as jest.Mock).mockResolvedValue(false);
const result = await findManifestFile();
expect(result).toBeUndefined();
expect(promptToProceedUploadFile).toHaveBeenCalledWith(mockFileName);
});

it('should return undefined if no manifest file is found', async () => {
(statSync as jest.Mock).mockReturnValueOnce(undefined);
const result = await findManifestFile();
expect(result).toBeUndefined();
});
});
2 changes: 1 addition & 1 deletion libs/report/ingestion/src/lib/ingestion-prompt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('promptClientName', () => {
const result = await promptClientName();

expect(input).toHaveBeenCalledWith({
message: 'Please enter your name:',
message: "Please enter your company's name:",
validate: expect.any(Function),
});
expect(result).toBe(mockName);
Expand Down
8 changes: 4 additions & 4 deletions libs/report/ingestion/src/lib/ingestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { findManifestFile, getClientToken, sendManifest } from './send-manifest'
import { type Options } from './types';

export const reportIngestionCommand: CommandModule<object, Options> = {
command: 'ingestion',
command: 'generate',
describe: 'send manifest files information',
aliases: ['ingest', 'i'],
aliases: ['gen', 'g'],
builder: {
consent: {
describe: 'Agree to understanding that sensitive data may be outputted',
describe: 'Agree to understanding that sensitive data may be sent to the server',
required: false,
default: false,
boolean: true,
Expand All @@ -35,5 +35,5 @@ async function run(args: ArgumentsCamelCase<Options>): Promise<void> {
}

await sendManifest(oid, manifest, { clientName });
console.log('Manifest sent correctly!');
console.log('Manifest sent successfully!');
}
2 changes: 1 addition & 1 deletion libs/report/ingestion/src/lib/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function askConsent(args: ArgumentsCamelCase<Options>): Promise<boo

export async function promptClientName() {
const name = await input({
message: 'Please enter your name:',
message: "Please enter your company's name:",
validate: (value) => (value.trim() === '' ? 'Name cannot be empty!' : true),
});
return name;
Expand Down
37 changes: 26 additions & 11 deletions libs/report/ingestion/src/lib/send-manifest.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
import { ApolloClient, InMemoryCache } from '@apollo/client/core';
import { readFileSync, statSync } from 'node:fs';
import { existsSync, readFileSync, statSync } from 'node:fs';
import { promptToProceedUploadFile } from './prompts';
import { TELEMETRY_INITIALIZE_MUTATION, TELEMETRY_REPORT_MUTATION } from './queries';
import { type JSONValue } from './types';
import { promptToProceedUploadFile } from './prompts';

const NES_REPORT_URL = process.env['NES_REPORT_URL'] || 'https://api.nes.herodevs.com/graphql';

const client = new ApolloClient({
cache: new InMemoryCache(),
uri: 'https://api.nes.herodevs.com/graphql',
uri: NES_REPORT_URL,
});

// I don't think this is the right key please change it :)
const MANIFEST_TELEMETRY_KEY = 'd7:diagnostics:report';
const MANIFEST_TELEMETRY_KEY = 'ingest:project:report';

// This is the list of files that we are going to look for
const MANIFEST_FILES = [{ name: 'package.json' }];

const MAX_FILE_SIZE = 5e6; // 5MB
const MAX_FILE_SIZE = 1024 * 1024; // 1MB

function getUserAgent() {
// This env var is set by npm when launching the script
// https://docs.npmjs.com/cli/v9/using-npm/scripts#packagejson-vars
return `hdcli/${process.env['npm_package_version'] ?? 'unknown'}`;
}

export async function initializeTelemetry(clientName: string) {
return client.mutate({
mutation: TELEMETRY_INITIALIZE_MUTATION,
variables: { clientName },
context: {
headers: {
'User-Agent': getUserAgent(),
},
},
});
}

Expand All @@ -36,6 +48,7 @@ export async function sendTelemetryReport(
context: {
headers: {
'x-nes-telrep': oid,
'User-Agent': getUserAgent(),
},
},
});
Expand Down Expand Up @@ -73,21 +86,23 @@ export async function sendManifest(
}

async function readManifestFile(fileName: string) {
const file = statSync(fileName);
// Does not exist
if (!file) {
const exists = existsSync(fileName);
if (!exists) {
console.log(`${fileName} not found`);
return;
}

const file = statSync(fileName);

// Empty file
if (file.size === 0) {
console.warn(`File ${fileName} is empty`);
console.warn(`[WARN] Ignoring file ${fileName}, because is empty`);
return;
}

// Cap the file size to prevent abuse
if (file.size > MAX_FILE_SIZE) {
console.warn(`File ${fileName} is too large`);
console.warn(`[WARN] Ignoring file ${fileName}, because it is too large`);
return;
}

Expand Down
1 change: 0 additions & 1 deletion libs/report/ingestion/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ export type JSONValue =
| JSONValue[];

export type Options = {
all: boolean;
consent: boolean;
};

0 comments on commit 4a88385

Please sign in to comment.