From fffb8b3345830adf5496c57c2841a868b079f0d4 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Wed, 8 Jan 2025 18:23:12 -0800 Subject: [PATCH] refactor lock file readers --- .changeset/silver-tables-do.md | 7 +- .eslint_dictionary.json | 1 + .../backend-deployer/src/cdk_deployer.test.ts | 1 + packages/cli-core/package.json | 3 +- .../npm_lock_file_reader.test.ts | 2 +- .../lock-file-reader/npm_lock_file_reader.ts | 19 ++-- .../pnpm_lock_file_reader.test.ts | 3 +- .../lock-file-reader/pnpm_lock_file_reader.ts | 17 ++-- .../yarn_classic_lock_file_reader.test.ts | 2 +- .../yarn_classic_lock_file_reader.ts | 17 ++-- .../yarn_modern_lock_file_reader.test.ts | 2 +- .../yarn_modern_lock_file_reader.ts | 17 ++-- .../npm_package_manager_controller.test.ts | 85 ++++++++++++++++++ .../npm_package_manager_controller.ts | 5 +- .../package_manager_controller_base.ts | 22 +++++ .../pnpm_package_manager_controller.test.ts | 85 ++++++++++++++++++ .../pnpm_package_manager_controller.ts | 5 +- ...classic_package_manager_controller.test.ts | 87 ++++++++++++++++++ ...yarn_classic_package_manager_controller.ts | 5 +- ..._modern_package_manager_controller.test.ts | 89 +++++++++++++++++++ .../yarn_modern_package_manager_controller.ts | 5 +- packages/cli/src/ampx.ts | 9 +- .../sandbox/sandbox_command_factory.ts | 16 +++- .../src/amplify_project_creator.test.ts | 1 + .../initial_project_file_generator.test.ts | 1 + packages/platform-core/API.md | 3 +- .../lock_file_reader_factory.test.ts | 59 ------------ .../lock_file_reader_factory.ts | 63 ------------- .../dependency_version_fetcher.test.ts | 75 ---------------- .../usage-data/dependency_version_fetcher.ts | 33 ------- .../src/usage-data/usage_data_emitter.test.ts | 36 +++----- .../src/usage-data/usage_data_emitter.ts | 10 +-- .../usage_data_emitter_factory.test.ts | 9 +- .../usage-data/usage_data_emitter_factory.ts | 8 +- packages/plugin-types/API.md | 17 ++++ .../src/package_manager_controller.ts | 11 +++ 36 files changed, 524 insertions(+), 306 deletions(-) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/npm_lock_file_reader.test.ts (96%) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/npm_lock_file_reader.ts (80%) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/pnpm_lock_file_reader.test.ts (96%) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/pnpm_lock_file_reader.ts (81%) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/yarn_classic_lock_file_reader.test.ts (99%) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/yarn_classic_lock_file_reader.ts (81%) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/yarn_modern_lock_file_reader.test.ts (99%) rename packages/{platform-core/src => cli-core/src/package-manager-controller}/lock-file-reader/yarn_modern_lock_file_reader.ts (81%) delete mode 100644 packages/platform-core/src/lock-file-reader/lock_file_reader_factory.test.ts delete mode 100644 packages/platform-core/src/lock-file-reader/lock_file_reader_factory.ts delete mode 100644 packages/platform-core/src/usage-data/dependency_version_fetcher.test.ts delete mode 100644 packages/platform-core/src/usage-data/dependency_version_fetcher.ts diff --git a/.changeset/silver-tables-do.md b/.changeset/silver-tables-do.md index eaa6ac2d01f..8029abf5cf9 100644 --- a/.changeset/silver-tables-do.md +++ b/.changeset/silver-tables-do.md @@ -1,5 +1,10 @@ --- -'@aws-amplify/platform-core': patch +'@aws-amplify/backend-deployer': patch +'create-amplify': patch +'@aws-amplify/backend-cli': patch +'@aws-amplify/cli-core': patch +'@aws-amplify/platform-core': minor +'@aws-amplify/plugin-types': minor --- Report cdk versions diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index fd595983187..c207869a7dd 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -100,6 +100,7 @@ "lang", "linux", "localhost", + "lockfile", "lsof", "lstat", "macos", diff --git a/packages/backend-deployer/src/cdk_deployer.test.ts b/packages/backend-deployer/src/cdk_deployer.test.ts index 24981682a44..39af9db7332 100644 --- a/packages/backend-deployer/src/cdk_deployer.test.ts +++ b/packages/backend-deployer/src/cdk_deployer.test.ts @@ -46,6 +46,7 @@ void describe('invokeCDKCommand', () => { runWithPackageManager: mock.fn(() => Promise.resolve() as never), getCommand: (args: string[]) => `'npx ${args.join(' ')}'`, allowsSignalPropagation: () => true, + getDependencies: mock.fn(() => Promise.resolve([])), }; const invoker = new CDKDeployer( diff --git a/packages/cli-core/package.json b/packages/cli-core/package.json index a735285a12f..266bbd5932a 100644 --- a/packages/cli-core/package.json +++ b/packages/cli-core/package.json @@ -22,6 +22,7 @@ "@aws-amplify/platform-core": "^1.3.0", "@inquirer/prompts": "^3.0.0", "execa": "^9.5.1", - "kleur": "^4.1.5" + "kleur": "^4.1.5", + "zod": "^3.22.2" } } diff --git a/packages/platform-core/src/lock-file-reader/npm_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.test.ts similarity index 96% rename from packages/platform-core/src/lock-file-reader/npm_lock_file_reader.test.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.test.ts index 91fdbbf07b7..fc1eadbec1d 100644 --- a/packages/platform-core/src/lock-file-reader/npm_lock_file_reader.test.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.test.ts @@ -2,7 +2,7 @@ import assert from 'assert'; import fsp from 'fs/promises'; import { afterEach, describe, it, mock } from 'node:test'; import path from 'path'; -import { NpmLockFileReader } from './npm_lock_file_reader'; +import { NpmLockFileReader } from './npm_lock_file_reader.js'; void describe('NpmLockFileReader', () => { const fspReadFileMock = mock.method(fsp, 'readFile', () => diff --git a/packages/platform-core/src/lock-file-reader/npm_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.ts similarity index 80% rename from packages/platform-core/src/lock-file-reader/npm_lock_file_reader.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.ts index fa3e56aa8f7..4aa3c811436 100644 --- a/packages/platform-core/src/lock-file-reader/npm_lock_file_reader.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/npm_lock_file_reader.ts @@ -1,18 +1,20 @@ -import fsp from 'fs/promises'; -import path from 'path'; -import z from 'zod'; import { - Dependencies, + Dependency, LockFileContents, LockFileReader, -} from './lock_file_reader_factory'; +} from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import z from 'zod'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; /** * NpmLockFileReader is an abstraction around the logic used to read and parse lock file contents */ export class NpmLockFileReader implements LockFileReader { getLockFileContentsFromCwd = async (): Promise => { - const dependencies: Dependencies = []; + const dependencies: Array = []; const packageLockJsonPath = path.resolve( process.cwd(), 'package-lock.json' @@ -23,7 +25,10 @@ export class NpmLockFileReader implements LockFileReader { const jsonLockContents = await fsp.readFile(packageLockJsonPath, 'utf-8'); jsonLockParsedValue = JSON.parse(jsonLockContents); } catch (error) { - // We failed to get lock file contents either because file doesn't exist or it is not parse-able + printer.log( + `Failed to get lock file contents because ${packageLockJsonPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); return { dependencies }; } diff --git a/packages/platform-core/src/lock-file-reader/pnpm_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.test.ts similarity index 96% rename from packages/platform-core/src/lock-file-reader/pnpm_lock_file_reader.test.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.test.ts index 3fef3d88c35..2c99265ff4a 100644 --- a/packages/platform-core/src/lock-file-reader/pnpm_lock_file_reader.test.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.test.ts @@ -2,13 +2,12 @@ import assert from 'assert'; import fsp from 'fs/promises'; import { afterEach, describe, it, mock } from 'node:test'; import path from 'path'; -import { PnpmLockFileReader } from './pnpm_lock_file_reader'; +import { PnpmLockFileReader } from './pnpm_lock_file_reader.js'; void describe('PnpmLockFileReader', () => { const fspReadFileMock = mock.method( fsp, 'readFile', - // eslint-disable-next-line spellcheck/spell-checker () => `lockfileVersion: '9.0' settings: diff --git a/packages/platform-core/src/lock-file-reader/pnpm_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.ts similarity index 81% rename from packages/platform-core/src/lock-file-reader/pnpm_lock_file_reader.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.ts index f637d48595e..86e1478881c 100644 --- a/packages/platform-core/src/lock-file-reader/pnpm_lock_file_reader.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/pnpm_lock_file_reader.ts @@ -1,10 +1,12 @@ -import fsp from 'fs/promises'; -import path from 'path'; import { - Dependencies, + Dependency, LockFileContents, LockFileReader, -} from './lock_file_reader_factory'; +} from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; /** * PnpmLockFileReader is an abstraction around the logic used to read and parse lock file contents @@ -12,7 +14,7 @@ import { export class PnpmLockFileReader implements LockFileReader { getLockFileContentsFromCwd = async (): Promise => { const eolRegex = '[\r\n]'; - const dependencies: Dependencies = []; + const dependencies: Array = []; const pnpmLockPath = path.resolve(process.cwd(), 'pnpm-lock.yaml'); try { @@ -43,7 +45,10 @@ export class PnpmLockFileReader implements LockFileReader { dependencies.push({ name: dependencyName, version: dependencyVersion }); } } catch (error) { - // We failed to get lock file contents either because file doesn't exist or it is not parse-able + printer.log( + `Failed to get lock file contents because ${pnpmLockPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); return { dependencies }; } diff --git a/packages/platform-core/src/lock-file-reader/yarn_classic_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.test.ts similarity index 99% rename from packages/platform-core/src/lock-file-reader/yarn_classic_lock_file_reader.test.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.test.ts index 2e82d588074..fb3585106e0 100644 --- a/packages/platform-core/src/lock-file-reader/yarn_classic_lock_file_reader.test.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.test.ts @@ -2,7 +2,7 @@ import assert from 'assert'; import fsp from 'fs/promises'; import { afterEach, describe, it, mock } from 'node:test'; import path from 'path'; -import { YarnClassicLockFileReader } from './yarn_classic_lock_file_reader'; +import { YarnClassicLockFileReader } from './yarn_classic_lock_file_reader.js'; void describe('YarnClassicLockFileReader', () => { const fspReadFileMock = mock.method( diff --git a/packages/platform-core/src/lock-file-reader/yarn_classic_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.ts similarity index 81% rename from packages/platform-core/src/lock-file-reader/yarn_classic_lock_file_reader.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.ts index 462640f58aa..8abc4779b18 100644 --- a/packages/platform-core/src/lock-file-reader/yarn_classic_lock_file_reader.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_classic_lock_file_reader.ts @@ -1,10 +1,12 @@ -import fsp from 'fs/promises'; -import path from 'path'; import { - Dependencies, + Dependency, LockFileContents, LockFileReader, -} from './lock_file_reader_factory'; +} from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; /** * YarnClassicLockFileReader is an abstraction around the logic used to read and parse lock file contents @@ -12,7 +14,7 @@ import { export class YarnClassicLockFileReader implements LockFileReader { getLockFileContentsFromCwd = async (): Promise => { const eolRegex = '[\r\n]'; - const dependencies: Dependencies = []; + const dependencies: Array = []; const yarnLockPath = path.resolve(process.cwd(), 'yarn.lock'); try { @@ -39,7 +41,10 @@ export class YarnClassicLockFileReader implements LockFileReader { dependencies.push({ name: dependencyName, version: dependencyVersion }); } } catch (error) { - // We failed to get lock file contents either because file doesn't exist or it is not parse-able + printer.log( + `Failed to get lock file contents because ${yarnLockPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); return { dependencies }; } diff --git a/packages/platform-core/src/lock-file-reader/yarn_modern_lock_file_reader.test.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.test.ts similarity index 99% rename from packages/platform-core/src/lock-file-reader/yarn_modern_lock_file_reader.test.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.test.ts index e7c575b30f0..1b1a5213a53 100644 --- a/packages/platform-core/src/lock-file-reader/yarn_modern_lock_file_reader.test.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.test.ts @@ -2,7 +2,7 @@ import assert from 'assert'; import fsp from 'fs/promises'; import { afterEach, describe, it, mock } from 'node:test'; import path from 'path'; -import { YarnModernLockFileReader } from './yarn_modern_lock_file_reader'; +import { YarnModernLockFileReader } from './yarn_modern_lock_file_reader.js'; void describe('YarnModernLockFileReader', () => { const fspReadFileMock = mock.method( diff --git a/packages/platform-core/src/lock-file-reader/yarn_modern_lock_file_reader.ts b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.ts similarity index 81% rename from packages/platform-core/src/lock-file-reader/yarn_modern_lock_file_reader.ts rename to packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.ts index e3e78ad2e88..3c8423f5e99 100644 --- a/packages/platform-core/src/lock-file-reader/yarn_modern_lock_file_reader.ts +++ b/packages/cli-core/src/package-manager-controller/lock-file-reader/yarn_modern_lock_file_reader.ts @@ -1,10 +1,12 @@ -import fsp from 'fs/promises'; -import path from 'path'; import { - Dependencies, + Dependency, LockFileContents, LockFileReader, -} from './lock_file_reader_factory'; +} from '@aws-amplify/plugin-types'; +import fsp from 'fs/promises'; +import path from 'path'; +import { printer } from '../../printer.js'; +import { LogLevel } from '../../printer/printer.js'; /** * YarnModernLockFileReader is an abstraction around the logic used to read and parse lock file contents @@ -12,7 +14,7 @@ import { export class YarnModernLockFileReader implements LockFileReader { getLockFileContentsFromCwd = async (): Promise => { const eolRegex = '[\r\n]'; - const dependencies: Dependencies = []; + const dependencies: Array = []; const yarnLockPath = path.resolve(process.cwd(), 'yarn.lock'); try { @@ -39,7 +41,10 @@ export class YarnModernLockFileReader implements LockFileReader { dependencies.push({ name: dependencyName, version: dependencyVersion }); } } catch (error) { - // We failed to get lock file contents either because file doesn't exist or it is not parse-able + printer.log( + `Failed to get lock file contents because ${yarnLockPath} does not exist or is not parse-able`, + LogLevel.DEBUG + ); return { dependencies }; } diff --git a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts index 938b1c580df..072bd1c14ca 100644 --- a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { execa } from 'execa'; import { NpmPackageManagerController } from './npm_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from '@aws-amplify/plugin-types'; void describe('NpmPackageManagerController', () => { const fspMock = { @@ -124,4 +125,88 @@ void describe('NpmPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 1); }); }); + + void describe('getDependencies', () => { + const existsSyncMock = mock.fn(() => true); + + void it('successfully returns dependency versions', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const npmPackageManagerController = new NpmPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await npmPackageManagerController.getDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + + void it('returns empty array if there are no matching dependencies', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const npmPackageManagerController = new NpmPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await npmPackageManagerController.getDependencies(); + + assert.deepEqual(dependencyVersions, []); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts index f680458be27..0277fbf7417 100644 --- a/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/npm_package_manager_controller.ts @@ -4,6 +4,7 @@ import { execa as _execa } from 'execa'; import * as _path from 'path'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { NpmLockFileReader } from './lock-file-reader/npm_lock_file_reader.js'; /** * NpmPackageManagerController is an abstraction around npm commands that are needed to initialize a project and install dependencies @@ -18,13 +19,15 @@ export class NpmPackageManagerController extends PackageManagerControllerBase { protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new NpmLockFileReader() ) { super( cwd, 'npm', ['init', '--yes'], 'install', + lockFileReader, fsp, path, execa, diff --git a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts index 642c8a54bf8..8946fec30e3 100644 --- a/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts +++ b/packages/cli-core/src/package-manager-controller/package_manager_controller_base.ts @@ -3,8 +3,10 @@ import _fsp from 'fs/promises'; import { execa as _execa } from 'execa'; import * as _path from 'path'; import { + Dependency, ExecaChildProcess, ExecaOptions, + LockFileReader, type PackageManagerController, } from '@aws-amplify/plugin-types'; import { LogLevel } from '../printer/printer.js'; @@ -27,6 +29,7 @@ export abstract class PackageManagerControllerBase protected readonly executable: string, protected readonly initDefault: string[], protected readonly installCommand: string, + protected readonly lockFileReader: LockFileReader, protected readonly fsp = _fsp, protected readonly path = _path, protected readonly execa = _execa, @@ -145,6 +148,25 @@ export abstract class PackageManagerControllerBase */ allowsSignalPropagation = () => true; + /** + * getDependencies - Retrieves dependency versions from the lock file in the project root + */ + getDependencies = async (): Promise> => { + const lockFileContents = + await this.lockFileReader.getLockFileContentsFromCwd(); + const targetDependencies = ['aws-cdk', 'aws-cdk-lib']; + + const dependencyVersions: Array = []; + + for (const { name, version } of lockFileContents.dependencies) { + if (targetDependencies.includes(name)) { + dependencyVersions.push({ name, version }); + } + } + + return dependencyVersions; + }; + /** * Check if a package.json file exists in projectRoot */ diff --git a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts index 3895e622513..83b631390fc 100644 --- a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { execa } from 'execa'; import { PnpmPackageManagerController } from './pnpm_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from '@aws-amplify/plugin-types'; void describe('PnpmPackageManagerController', () => { const fspMock = { @@ -124,4 +125,88 @@ void describe('PnpmPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 1); }); }); + + void describe('getDependencies', () => { + const existsSyncMock = mock.fn(() => true); + + void it('successfully returns dependency versions', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const pnpmPackageManagerController = new PnpmPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await pnpmPackageManagerController.getDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + + void it('returns empty array if there are no matching dependencies', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const pnpmPackageManagerController = new PnpmPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await pnpmPackageManagerController.getDependencies(); + + assert.deepEqual(dependencyVersions, []); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts index 5eb13ea7de3..2cb6fb49336 100644 --- a/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/pnpm_package_manager_controller.ts @@ -4,6 +4,7 @@ import { existsSync as _existsSync } from 'fs'; import { execa as _execa } from 'execa'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { PnpmLockFileReader } from './lock-file-reader/pnpm_lock_file_reader.js'; /** * PnpmPackageManagerController is an abstraction around pnpm commands that are needed to initialize a project and install dependencies @@ -18,13 +19,15 @@ export class PnpmPackageManagerController extends PackageManagerControllerBase { protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new PnpmLockFileReader() ) { super( cwd, 'pnpm', ['init'], 'install', + lockFileReader, fsp, path, execa, diff --git a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts index 28987b2784b..00ac91fc6ef 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { execa } from 'execa'; import { YarnClassicPackageManagerController } from './yarn_classic_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from '@aws-amplify/plugin-types'; void describe('YarnClassicPackageManagerController', () => { const fspMock = { @@ -128,4 +129,90 @@ void describe('YarnClassicPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 1); }); }); + + void describe('getDependencies', () => { + const existsSyncMock = mock.fn(() => true); + + void it('successfully returns dependency versions', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const yarnClassicPackageManagerController = + new YarnClassicPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await yarnClassicPackageManagerController.getDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + + void it('returns empty array if there are no matching dependencies', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const yarnClassicPackageManagerController = + new YarnClassicPackageManagerController( + '/testProjectRoot', + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await yarnClassicPackageManagerController.getDependencies(); + + assert.deepEqual(dependencyVersions, []); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts index 48a4330ed96..70c8e451546 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_classic_package_manager_controller.ts @@ -4,6 +4,7 @@ import { execa as _execa } from 'execa'; import * as _path from 'path'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { YarnClassicLockFileReader } from './lock-file-reader/yarn_classic_lock_file_reader.js'; /** * YarnClassicPackageManagerController is an abstraction around yarn classic commands that are needed to initialize a project and install dependencies @@ -18,13 +19,15 @@ export class YarnClassicPackageManagerController extends PackageManagerControlle protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new YarnClassicLockFileReader() ) { super( cwd, 'yarn', ['init', '--yes'], 'add', + lockFileReader, fsp, path, execa, diff --git a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts index 4dfac585d3c..215c5739484 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.test.ts @@ -6,6 +6,7 @@ import { execa } from 'execa'; import { Printer } from '../printer/printer.js'; import { YarnModernPackageManagerController } from './yarn_modern_package_manager_controller.js'; import { executeWithDebugLogger } from './execute_with_debugger_logger.js'; +import { LockFileReader } from '@aws-amplify/plugin-types'; void describe('YarnModernPackageManagerController', () => { const fspMock = { @@ -123,4 +124,92 @@ void describe('YarnModernPackageManagerController', () => { assert.equal(fspMock.writeFile.mock.callCount(), 2); }); }); + + void describe('getDependencies', () => { + const existsSyncMock = mock.fn(() => true); + + void it('successfully returns dependency versions', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const yarnModernPackageManagerController = + new YarnModernPackageManagerController( + '/testProjectRoot', + printerMock as unknown as Printer, + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await yarnModernPackageManagerController.getDependencies(); + const expectedVersions = [ + { + name: 'aws-cdk', + version: '1.2.3', + }, + { + name: 'aws-cdk-lib', + version: '12.13.14', + }, + ]; + + assert.deepEqual(dependencyVersions, expectedVersions); + }); + + void it('returns empty array if there are no matching dependencies', async () => { + const lockFileReaderMock = { + getLockFileContentsFromCwd: async () => + Promise.resolve({ + dependencies: [ + { + name: 'test_dep', + version: '1.23.45', + }, + { + name: 'some_other_dep', + version: '12.1.3', + }, + ], + }), + } as LockFileReader; + const yarnModernPackageManagerController = + new YarnModernPackageManagerController( + '/testProjectRoot', + printerMock as unknown as Printer, + fspMock as unknown as typeof fsp, + pathMock as unknown as typeof path, + execaMock as unknown as typeof execa, + executeWithDebugLoggerMock as unknown as typeof executeWithDebugLogger, + existsSyncMock, + lockFileReaderMock + ); + const dependencyVersions = + await yarnModernPackageManagerController.getDependencies(); + + assert.deepEqual(dependencyVersions, []); + }); + }); }); diff --git a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts index 0188ec889de..f4d6003f1b8 100644 --- a/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts +++ b/packages/cli-core/src/package-manager-controller/yarn_modern_package_manager_controller.ts @@ -6,6 +6,7 @@ import { LogLevel, Printer } from '../printer/printer.js'; import { format } from '../format/format.js'; import { executeWithDebugLogger as _executeWithDebugLogger } from './execute_with_debugger_logger.js'; import { PackageManagerControllerBase } from './package_manager_controller_base.js'; +import { YarnModernLockFileReader } from './lock-file-reader/yarn_modern_lock_file_reader.js'; /** * YarnModernPackageManagerController is an abstraction around yarn modern (yarn v2+) commands that are needed to initialize a project and install dependencies @@ -21,13 +22,15 @@ export class YarnModernPackageManagerController extends PackageManagerController protected readonly path = _path, protected readonly execa = _execa, protected readonly executeWithDebugLogger = _executeWithDebugLogger, - protected readonly existsSync = _existsSync + protected readonly existsSync = _existsSync, + protected readonly lockFileReader = new YarnModernLockFileReader() ) { super( cwd, 'yarn', ['init', '--yes'], 'add', + lockFileReader, fsp, path, execa, diff --git a/packages/cli/src/ampx.ts b/packages/cli/src/ampx.ts index 2ddb90ccb26..8d97e526a17 100755 --- a/packages/cli/src/ampx.ts +++ b/packages/cli/src/ampx.ts @@ -13,7 +13,7 @@ import { import { fileURLToPath } from 'node:url'; import { verifyCommandName } from './verify_command_name.js'; import { hideBin } from 'yargs/helpers'; -import { format } from '@aws-amplify/cli-core'; +import { PackageManagerControllerFactory, format } from '@aws-amplify/cli-core'; const packageJson = new PackageJsonReader().read( fileURLToPath(new URL('../package.json', import.meta.url)) @@ -27,8 +27,13 @@ if (libraryVersion == undefined) { }); } +const dependencies = await new PackageManagerControllerFactory() + .getPackageManagerController() + .getDependencies(); + const usageDataEmitter = await new UsageDataEmitterFactory().getInstance( - libraryVersion + libraryVersion, + dependencies ); attachUnhandledExceptionListeners(usageDataEmitter); diff --git a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts index ef5454a9319..c63403f9478 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts @@ -16,7 +16,11 @@ import { } from '@aws-amplify/platform-core'; import { SandboxEventHandlerFactory } from './sandbox_event_handler_factory.js'; import { CommandMiddleware } from '../../command_middleware.js'; -import { format, printer } from '@aws-amplify/cli-core'; +import { + PackageManagerControllerFactory, + format, + printer, +} from '@aws-amplify/cli-core'; import { S3Client } from '@aws-sdk/client-s3'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; @@ -57,7 +61,15 @@ export const createSandboxCommand = (): CommandModule< const eventHandlerFactory = new SandboxEventHandlerFactory( sandboxBackendIdPartsResolver.resolve, - async () => await new UsageDataEmitterFactory().getInstance(libraryVersion) + async () => { + const dependencies = await new PackageManagerControllerFactory() + .getPackageManagerController() + .getDependencies(); + return await new UsageDataEmitterFactory().getInstance( + libraryVersion, + dependencies + ); + } ); const commandMiddleWare = new CommandMiddleware(printer); diff --git a/packages/create-amplify/src/amplify_project_creator.test.ts b/packages/create-amplify/src/amplify_project_creator.test.ts index ea9407ebfb8..a88c08efcbe 100644 --- a/packages/create-amplify/src/amplify_project_creator.test.ts +++ b/packages/create-amplify/src/amplify_project_creator.test.ts @@ -109,6 +109,7 @@ void describe('AmplifyProjectCreator', () => { runWithPackageManager: mock.fn(() => Promise.resolve() as never), getCommand: (args: string[]) => `'npx ${args.join(' ')}'`, allowsSignalPropagation: () => true, + getDependencies: mock.fn(() => Promise.resolve([])), }; const projectRootValidatorMock = { validate: mock.fn() }; const gitIgnoreInitializerMock = { ensureInitialized: mock.fn() }; diff --git a/packages/create-amplify/src/initial_project_file_generator.test.ts b/packages/create-amplify/src/initial_project_file_generator.test.ts index 8fcfb9190ac..4d5314e680f 100644 --- a/packages/create-amplify/src/initial_project_file_generator.test.ts +++ b/packages/create-amplify/src/initial_project_file_generator.test.ts @@ -17,6 +17,7 @@ void describe('InitialProjectFileGenerator', () => { runWithPackageManager: mock.fn(() => Promise.resolve() as never), getCommand: (args: string[]) => `'npx ${args.join(' ')}'`, allowsSignalPropagation: () => true, + getDependencies: mock.fn(() => Promise.resolve([])), }; beforeEach(() => { executeWithDebugLoggerMock.mock.resetCalls(); diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 06891c5af75..092d8aee7ff 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -8,6 +8,7 @@ import { AppId } from '@aws-amplify/plugin-types'; import { ApplicationLogLevel } from 'aws-cdk-lib/aws-lambda'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import { DeepPartialAmplifyGeneratedConfigs } from '@aws-amplify/plugin-types'; +import { Dependency } from '@aws-amplify/plugin-types'; import { FieldLogLevel } from 'aws-cdk-lib/aws-appsync'; import { LogLevel } from '@aws-amplify/plugin-types'; import { LogRetention } from '@aws-amplify/plugin-types'; @@ -230,7 +231,7 @@ export type UsageDataEmitter = { // @public export class UsageDataEmitterFactory { - getInstance: (libraryVersion: string) => Promise; + getInstance: (libraryVersion: string, dependencies: Array) => Promise; } // (No @packageDocumentation comment for this package) diff --git a/packages/platform-core/src/lock-file-reader/lock_file_reader_factory.test.ts b/packages/platform-core/src/lock-file-reader/lock_file_reader_factory.test.ts deleted file mode 100644 index 302f1bf6a85..00000000000 --- a/packages/platform-core/src/lock-file-reader/lock_file_reader_factory.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import assert from 'assert'; -import { afterEach, beforeEach, describe, it } from 'node:test'; -import { NpmLockFileReader } from './npm_lock_file_reader'; -import { PnpmLockFileReader } from './pnpm_lock_file_reader'; -import { YarnClassicLockFileReader } from './yarn_classic_lock_file_reader'; -import { YarnModernLockFileReader } from './yarn_modern_lock_file_reader'; -import { LockFileReaderFactory } from './lock_file_reader_factory'; - -void describe('LockFileReaderFactory', () => { - let originalEnv: NodeJS.ProcessEnv; - - beforeEach(() => { - originalEnv = process.env; - }); - - afterEach(() => { - process.env = originalEnv; - }); - - void describe('getLockFileReader', () => { - const testCases = [ - { - name: 'npm', - userAgent: 'npm/7.0.0 node/v15.0.0 darwin x64', - expectedInstanceOf: NpmLockFileReader, - }, - { - name: 'pnpm', - userAgent: 'pnpm/5.0.0 node/v15.0.0 darwin x64', - expectedInstanceOf: PnpmLockFileReader, - }, - { - name: 'yarn classic', - userAgent: 'yarn/1.22.21 node/v15.0.0 darwin x64', - expectedInstanceOf: YarnClassicLockFileReader, - }, - { - name: 'yarn modern', - userAgent: 'yarn/4.0.1 node/v15.0.0 darwin x64', - expectedInstanceOf: YarnModernLockFileReader, - }, - ]; - - for (const testCase of testCases) { - void it(`should return the correct lock file reader for ${testCase.name}`, () => { - process.env.npm_config_user_agent = testCase.userAgent; - const lockFileReader = new LockFileReaderFactory().getLockFileReader(); - assert.ok(lockFileReader instanceof testCase.expectedInstanceOf); - }); - } - - void it('defaults to npm lock file reader', () => { - process.env.npm_config_user_agent = - 'unsupported/1.0.0 node/v15.0.0 darwin x64'; - const lockFileReader = new LockFileReaderFactory().getLockFileReader(); - assert.ok(lockFileReader instanceof NpmLockFileReader); - }); - }); -}); diff --git a/packages/platform-core/src/lock-file-reader/lock_file_reader_factory.ts b/packages/platform-core/src/lock-file-reader/lock_file_reader_factory.ts deleted file mode 100644 index c3957737fbd..00000000000 --- a/packages/platform-core/src/lock-file-reader/lock_file_reader_factory.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { NpmLockFileReader } from './npm_lock_file_reader'; -import { PnpmLockFileReader } from './pnpm_lock_file_reader'; -import { YarnClassicLockFileReader } from './yarn_classic_lock_file_reader'; -import { YarnModernLockFileReader } from './yarn_modern_lock_file_reader'; - -/** - * LockFileReaderFactory is a factory for an abstraction around reading lock files from different package managers - */ -export class LockFileReaderFactory { - /** - * Creates a lock file reader factory - */ - constructor(private readonly platform = process.platform) {} - - /** - * Gets the lock file reader based on the package manager being used - */ - getLockFileReader(): LockFileReader { - const packageManagerName = this.getPackageManagerName(); - switch (packageManagerName) { - case 'npm': - return new NpmLockFileReader(); - case 'pnpm': - return new PnpmLockFileReader(); - case 'yarn-classic': - return new YarnClassicLockFileReader(); - case 'yarn-modern': - return new YarnModernLockFileReader(); - default: - // defaults to npm lock file reader as it is the most used package manager by customers - return new NpmLockFileReader(); - } - } - - /** - * Get package manager name from npm_config_user_agent - */ - private getPackageManagerName() { - const userAgent = process.env.npm_config_user_agent; - if (userAgent === undefined) { - return; - } - const packageManagerAndVersion = userAgent.split(' ')[0]; - const [packageManagerName, packageManagerVersion] = - packageManagerAndVersion.split('/'); - - if (packageManagerName === 'yarn') { - const yarnMajorVersion = packageManagerVersion.split('.')[0]; - return `yarn-${yarnMajorVersion === '1' ? 'classic' : 'modern'}`; - } - return packageManagerName; - } -} - -export type LockFileReader = { - getLockFileContentsFromCwd: () => Promise; -}; - -export type LockFileContents = { - dependencies: Dependencies; -}; - -export type Dependencies = Array<{ name: string; version: string }>; diff --git a/packages/platform-core/src/usage-data/dependency_version_fetcher.test.ts b/packages/platform-core/src/usage-data/dependency_version_fetcher.test.ts deleted file mode 100644 index 2764b203262..00000000000 --- a/packages/platform-core/src/usage-data/dependency_version_fetcher.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import assert from 'assert'; -import { describe, it } from 'node:test'; -import { DependencyVersionFetcher } from './dependency_version_fetcher'; -import { LockFileReader } from '../lock-file-reader/lock_file_reader_factory'; - -void describe('getDependencyVersions', () => { - void it('successfully returns dependency versions', async () => { - const lockFileReaderMock = { - getLockFileContentsFromCwd: async () => - Promise.resolve({ - dependencies: [ - { - name: 'aws-cdk', - version: '1.2.3', - }, - { - name: 'aws-cdk-lib', - version: '12.13.14', - }, - { - name: 'test_dep', - version: '1.23.45', - }, - { - name: 'some_other_dep', - version: '12.1.3', - }, - ], - }), - } as LockFileReader; - const dependencyVersionFetcher = new DependencyVersionFetcher( - lockFileReaderMock - ); - const dependencyVersions = - await dependencyVersionFetcher.getDependencyVersions(); - const expectedVersions = [ - { - name: 'aws-cdk', - version: '1.2.3', - }, - { - name: 'aws-cdk-lib', - version: '12.13.14', - }, - ]; - - assert.deepEqual(dependencyVersions, expectedVersions); - }); - - void it('returns empty array if there are no matching dependencies', async () => { - const lockFileReaderMock = { - getLockFileContentsFromCwd: async () => - Promise.resolve({ - dependencies: [ - { - name: 'test_dep', - version: '1.23.45', - }, - { - name: 'some_other_dep', - version: '12.1.3', - }, - ], - }), - } as LockFileReader; - - const dependencyVersionFetcher = new DependencyVersionFetcher( - lockFileReaderMock - ); - const dependencyVersions = - await dependencyVersionFetcher.getDependencyVersions(); - - assert.deepEqual(dependencyVersions, []); - }); -}); diff --git a/packages/platform-core/src/usage-data/dependency_version_fetcher.ts b/packages/platform-core/src/usage-data/dependency_version_fetcher.ts deleted file mode 100644 index 6d47e19e044..00000000000 --- a/packages/platform-core/src/usage-data/dependency_version_fetcher.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - Dependencies, - LockFileReader, - LockFileReaderFactory, -} from '../lock-file-reader/lock_file_reader_factory'; - -/** - * Get dependency versions from customer project's lock file - */ -export class DependencyVersionFetcher { - /** - * Creates dependency version fetcher - */ - constructor( - private readonly lockFileReader: LockFileReader = new LockFileReaderFactory().getLockFileReader() - ) {} - - getDependencyVersions = async () => { - const lockFileContents = - await this.lockFileReader.getLockFileContentsFromCwd(); - const targetDependencies = ['aws-cdk', 'aws-cdk-lib']; - - const dependencyVersions: Dependencies = []; - - for (const { name, version } of lockFileContents.dependencies) { - if (targetDependencies.includes(name)) { - dependencyVersions.push({ name, version }); - } - } - - return dependencyVersions; - }; -} diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts index 730ef161a67..281fa691420 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts @@ -11,8 +11,6 @@ import { AccountIdFetcher } from './account_id_fetcher'; import { UsageData } from './usage_data'; import isCI from 'is-ci'; import { AmplifyError, AmplifyUserError } from '..'; -import { LockFileReader } from '../lock-file-reader/lock_file_reader_factory'; -import { DependencyVersionFetcher } from './dependency_version_fetcher'; const originalNpmUserAgent = process.env.npm_config_user_agent; const testNpmUserAgent = 'testNpmUserAgent'; @@ -21,6 +19,16 @@ void describe('UsageDataEmitter', () => { let usageDataEmitter: DefaultUsageDataEmitter; const testLibraryVersion = '1.2.3'; + const testDependencies = [ + { + name: 'test-dep', + version: '1.2.3', + }, + { + name: 'some_other_dep', + version: '12.13.14', + }, + ]; const testURL = url.parse('https://aws.amazon.com/amplify/'); const onReqEndMock = mock.fn(); const onReqWriteMock = mock.fn(); @@ -42,26 +50,6 @@ void describe('UsageDataEmitter', () => { fetch: async () => '123456789012', } as AccountIdFetcher; - // For DependencyVersionFetcher - const lockFileReaderMock = { - getLockFileContentsFromCwd: async () => - Promise.resolve({ - dependencies: [ - { - name: 'aws-cdk', - version: '1.2.4', - }, - { - name: 'test_dep', - version: '12.13.14', - }, - ], - }), - } as LockFileReader; - const dependencyVersionFetcherMock = new DependencyVersionFetcher( - lockFileReaderMock - ); - mock.method(https, 'request', () => reqMock); before(() => { @@ -177,10 +165,10 @@ void describe('UsageDataEmitter', () => { usageDataEmitter = new DefaultUsageDataEmitter( testLibraryVersion, + testDependencies, v4(), testURL, - accountIdFetcherMock, - dependencyVersionFetcherMock + accountIdFetcherMock ); let usageDataEmitterPromise; diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.ts b/packages/platform-core/src/usage-data/usage_data_emitter.ts index 92b871b8ef7..653fb4f513f 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.ts @@ -10,7 +10,7 @@ import isCI from 'is-ci'; import { SerializableError } from './serializable_error.js'; import { UsageDataEmitter } from './usage_data_emitter_factory.js'; import { AmplifyError } from '../index.js'; -import { DependencyVersionFetcher } from './dependency_version_fetcher.js'; +import { Dependency } from '@aws-amplify/plugin-types'; /** * Entry point for sending usage data metrics @@ -21,10 +21,10 @@ export class DefaultUsageDataEmitter implements UsageDataEmitter { */ constructor( private readonly libraryVersion: string, + private readonly dependencies: Array, private readonly sessionUuid = uuid(), private readonly url = getUrl(), - private readonly accountIdFetcher = new AccountIdFetcher(), - private readonly dependencyVersionFetcher = new DependencyVersionFetcher() + private readonly accountIdFetcher = new AccountIdFetcher() ) {} emitSuccess = async ( @@ -90,9 +90,7 @@ export class DefaultUsageDataEmitter implements UsageDataEmitter { isCi: isCI, projectSetting: { editor: process.env.npm_config_user_agent, - details: JSON.stringify( - await this.dependencyVersionFetcher.getDependencyVersions() - ), + details: JSON.stringify(this.dependencies), }, }; }; diff --git a/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts b/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts index 7d7e0aaf84e..443a0227e99 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter_factory.test.ts @@ -41,7 +41,8 @@ void describe('UsageDataEmitterFactory', () => { void it('returns DefaultUsageDataEmitter by default', async () => { configControllerGet.mock.mockImplementationOnce(() => undefined); const dataEmitter = await new UsageDataEmitterFactory().getInstance( - '0.0.0' + '0.0.0', + [] ); assert.strictEqual(configControllerGet.mock.callCount(), 1); assert.strictEqual(dataEmitter instanceof DefaultUsageDataEmitter, true); @@ -51,7 +52,8 @@ void describe('UsageDataEmitterFactory', () => { configControllerGet.mock.mockImplementationOnce(() => undefined); process.env['AMPLIFY_DISABLE_TELEMETRY'] = '1'; const dataEmitter = await new UsageDataEmitterFactory().getInstance( - '0.0.0' + '0.0.0', + [] ); assert.strictEqual(dataEmitter instanceof NoOpUsageDataEmitter, true); assert.strictEqual(configControllerGet.mock.callCount(), 1); @@ -61,7 +63,8 @@ void describe('UsageDataEmitterFactory', () => { void it('returns NoOpUsageDataEmitter if local config file exists and reads true', async () => { configControllerGet.mock.mockImplementationOnce(() => false); const dataEmitter = await new UsageDataEmitterFactory().getInstance( - '0.0.0' + '0.0.0', + [] ); assert.strictEqual(configControllerGet.mock.callCount(), 1); assert.strictEqual(dataEmitter instanceof NoOpUsageDataEmitter, true); diff --git a/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts b/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts index 6ee9a2fb110..f4466f1e833 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts @@ -3,6 +3,7 @@ import { NoOpUsageDataEmitter } from './noop_usage_data_emitter.js'; import { DefaultUsageDataEmitter } from './usage_data_emitter.js'; import { USAGE_DATA_TRACKING_ENABLED } from './constants.js'; import { AmplifyError } from '../index.js'; +import { Dependency } from '@aws-amplify/plugin-types'; export type UsageDataEmitter = { emitSuccess: ( @@ -22,7 +23,10 @@ export class UsageDataEmitterFactory { /** * Creates UsageDataEmitter for a given library version, usage data tracking preferences */ - getInstance = async (libraryVersion: string): Promise => { + getInstance = async ( + libraryVersion: string, + dependencies: Array + ): Promise => { const configController = configControllerFactory.getInstance( 'usage_data_preferences.json' ); @@ -37,6 +41,6 @@ export class UsageDataEmitterFactory { ) { return new NoOpUsageDataEmitter(); } - return new DefaultUsageDataEmitter(libraryVersion); + return new DefaultUsageDataEmitter(libraryVersion, dependencies); }; } diff --git a/packages/plugin-types/API.md b/packages/plugin-types/API.md index 181acf42420..e6ecbae1b5e 100644 --- a/packages/plugin-types/API.md +++ b/packages/plugin-types/API.md @@ -152,6 +152,12 @@ export type DeepPartialAmplifyGeneratedConfigs = { [P in keyof T]?: P extends 'auth' | 'data' | 'storage' ? T[P] extends object ? DeepPartialAmplifyGeneratedConfigs : Partial : T[P]; }; +// @public (undocumented) +export type Dependency = { + name: string; + version: string; +}; + // @public export type DeploymentType = 'branch' | 'sandbox'; @@ -196,6 +202,16 @@ export type ImportPathVerifier = { verify: (importStack: string | undefined, expectedImportingFile: string, errorMessage: string) => void; }; +// @public (undocumented) +export type LockFileContents = { + dependencies: Array; +}; + +// @public (undocumented) +export type LockFileReader = { + getLockFileContentsFromCwd: () => Promise; +}; + // @public (undocumented) export type LogLevel = 'all' | 'debug' | 'error' | 'fatal' | 'info' | 'none' | 'trace' | 'warn'; @@ -220,6 +236,7 @@ export type PackageManagerController = { runWithPackageManager: (args: string[] | undefined, dir: string, options?: ExecaOptions) => ExecaChildProcess; getCommand: (args: string[]) => string; allowsSignalPropagation: () => boolean; + getDependencies: () => Promise>; }; // @public (undocumented) diff --git a/packages/plugin-types/src/package_manager_controller.ts b/packages/plugin-types/src/package_manager_controller.ts index 8e53789641e..9eed9e1e80a 100644 --- a/packages/plugin-types/src/package_manager_controller.ts +++ b/packages/plugin-types/src/package_manager_controller.ts @@ -23,6 +23,16 @@ export type ExecaChildProcess = { stderr: Readable | null; } & Promise; +export type Dependency = { name: string; version: string }; + +export type LockFileContents = { + dependencies: Array; +}; + +export type LockFileReader = { + getLockFileContentsFromCwd: () => Promise; +}; + export type PackageManagerController = { initializeProject: () => Promise; initializeTsConfig: (targetDir: string) => Promise; @@ -37,4 +47,5 @@ export type PackageManagerController = { ) => ExecaChildProcess; getCommand: (args: string[]) => string; allowsSignalPropagation: () => boolean; + getDependencies: () => Promise>; };