Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add DependencyMetric and NpmScope #53

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
df83e9a
feat: add DependencyMetric and NpmScope
francinelucca Aug 24, 2023
805afee
Merge branch 'main' of github.com:carbon-design-system/telemetrics-js…
francinelucca Aug 30, 2023
ed257be
fix: import paths fix
francinelucca Aug 30, 2023
18ae8e3
Merge branch 'main' into 51-npm-scope-generate-capturable-metrics-fro…
francinelucca Aug 31, 2023
da130c6
Merge branch 'main' of github.com:carbon-design-system/telemetrics-js…
francinelucca Sep 1, 2023
9cb8202
Merge branch '51-npm-scope-generate-capturable-metrics-from-dependenc…
francinelucca Sep 1, 2023
1434256
fix: use semver to get package version details, adjust test
francinelucca Sep 1, 2023
8ef0606
feat: rework from brainstorming session
jdharvey-ibm Sep 6, 2023
5147211
feat: add getProjectRoot function
francinelucca Sep 7, 2023
b0210c7
feat: add getPackageName function
francinelucca Sep 7, 2023
c824b05
Merge branch 'main' of github.com:carbon-design-system/telemetrics-js…
francinelucca Sep 7, 2023
f5d92dd
fix: refactor getProjectRoot and cleanup old functions
francinelucca Sep 7, 2023
29586b9
refactor: getInstallingPackages refactor
francinelucca Sep 8, 2023
f278722
test: preliminary tests for npm scope auxiliary functions
francinelucca Sep 8, 2023
de9cc01
fix: online brainstorming rework
jdharvey-ibm Sep 8, 2023
4b6cb1c
fix: factor out some npm scope logic to simplify functions
jdharvey-ibm Sep 11, 2023
923c583
fix(npm): add async to function returning a promise
jdharvey-ibm Sep 11, 2023
995efed
test: unit tests for new npm scope functions and small refactor
francinelucca Sep 11, 2023
e51a8f6
test(npm): reorganize some tests and fixtures
jdharvey-ibm Sep 12, 2023
019979e
fix: move getProjectRoot, add scope tests, add docs
jdharvey-ibm Sep 12, 2023
2c1efd0
refactor: rename find-installers to find-installers-from-tree
jdharvey-ibm Sep 12, 2023
27f5022
test: rewrite npmScope test
francinelucca Sep 12, 2023
6b81cc7
test: refactor
francinelucca Sep 12, 2023
b8f9092
fix: remove no longer used exception
francinelucca Sep 12, 2023
a3aa497
feat: change startTime and endTime in data points to always be zero
jdharvey-ibm Sep 12, 2023
6aca1b6
Merge branch 'main' into 51-npm-scope-generate-capturable-metrics-fro…
jdharvey-ibm Sep 12, 2023
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
12 changes: 9 additions & 3 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ module.exports = {
'plugin:@typescript-eslint/strict',
'plugin:eslint-comments/recommended',
'plugin:jsdoc/recommended-typescript',
'plugin:n/recommended',
'plugin:vitest/all'
'plugin:n/recommended'
],
overrides: [
// Special rules for config files
{
env: {
node: true
Expand All @@ -33,14 +33,20 @@ module.exports = {
rules: {
'@typescript-eslint/no-var-requires': 'off'
}
},
// Special rules for test files
{
files: ['**/*.test.ts'],
plugins: ['vitest'],
extends: ['plugin:vitest/all']
}
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: 'tsconfig.eslint.json'
},
plugins: ['jsdoc', 'notice', 'simple-import-sort', 'vitest'],
plugins: ['jsdoc', 'notice', 'simple-import-sort'],
root: true,
rules: {
'@typescript-eslint/explicit-function-return-type': 'off',
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ src/schemas/telemetrics-config.schema.json

# Dotenv stuff
.env

# Allow node_modules folders in test fixtures
!src/test/__fixtures/projects/**/node_modules
41 changes: 20 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,18 @@
"@opentelemetry/semantic-conventions": "^1.15.2",
"ajv": "^8.12.0",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"object-scan": "^19.0.2",
"reflect-metadata": "^0.1.13",
"semver": "^7.5.4",
"tmp-promise": "^3.0.3"
},
"devDependencies": {
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@types/js-yaml": "^4.0.5",
"@types/node": "^18.17.12",
"@types/lodash": "^4.14.198",
"@typescript-eslint/eslint-plugin": "^6.5.0",
"@vitest/coverage-v8": "^0.34.3",
"eslint": "^8.46.0",
Expand Down
17 changes: 17 additions & 0 deletions src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright IBM Corp. 2023, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
declare module 'object-scan' {
export type ObjectPath = string[]

export default function objectScan(
query: string[],
options: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- module types not owned by us
filterFn: (obj: { value: any }) => boolean
}
): (obj: unknown) => ObjectPath[]
}
14 changes: 10 additions & 4 deletions src/main/core/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,30 @@
* LICENSE file in the root directory of this source tree.
*/
import childProcess from 'node:child_process'
import util from 'node:util'

import { guardShell } from './guard-shell.js'

const runCommand = util.promisify(childProcess.exec)

/**
* Executes a command using childProcess. Throws an exception if the command fails.
* Executes a command using childProcess. Throws an exception if the command fails or exits with a
* non-zero exit code.
*
* @param cmd - The command to invoke.
* @param options - Options to include along with the command.
* @throws An exception if the command timed out or exited with a non-zero code.
* @throws An error if the command timed out or exited with a non-zero code.
* @returns The standard output from the command.
*/
export function exec(cmd: string, options: childProcess.ExecSyncOptionsWithBufferEncoding = {}) {
export async function exec(cmd: string, options: childProcess.ExecOptions = {}) {
guardShell(cmd)

const execOptions = {
env: process.env,
...options
}

return childProcess.execSync(cmd, execOptions).toString().trim()
const proc = await runCommand(cmd, execOptions)

return proc.stdout.toString().trim()
}
18 changes: 18 additions & 0 deletions src/main/core/get-project-root.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright IBM Corp. 2023, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { exec } from './exec.js'

/**
* Finds and returns the root-most directory of the analyzed project's source tree.
*
* @param cwd - Current working directory to use as the basis for finding the root directory.
* @throws An exception if no usable root data was obtained.
* @returns The path of the analyzed project's root directory or null.
*/
export async function getProjectRoot(cwd: string): Promise<string> {
return await exec('git rev-parse --show-toplevel', { cwd })
}
6 changes: 3 additions & 3 deletions src/main/core/guard-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/

import { GuardShellError } from '../exceptions/guard-shell-error.js'

/**
* Checks a command for certain disallowed characters and throws an exception if they are found.
*
Expand All @@ -13,8 +15,6 @@
*/
export function guardShell(cmd: string) {
if (/[\\$;`]/.exec(cmd) != null) {
throw new Error(
'Shell guard prevented a command from running because it contained special characters: ' + cmd
)
throw new GuardShellError('Command contained forbidden characters: ' + cmd)
}
}
9 changes: 5 additions & 4 deletions src/main/core/log/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
import 'reflect-metadata'

import { LoggerNotFoundError } from '../../exceptions/logger-not-found-error.js'
import { type Loggable } from './loggable.js'
import { Logger } from './logger.js'
import { safeStringify } from './safe-stringify.js'
Expand Down Expand Up @@ -48,7 +49,7 @@ function Trace(): MethodDecorator {
const logger: unknown = this.logger

if (!(logger instanceof Logger)) {
throw new Error('Attempt to trace method without a defined logger instance')
throw new LoggerNotFoundError()
}

setImmediate(() => {
Expand All @@ -58,12 +59,12 @@ function Trace(): MethodDecorator {
let result: unknown
try {
result = original.apply(this, args)
} catch (err) {
} catch (e) {
setImmediate(() => {
void traceExit(logger, String(propertyKey), err)
void traceExit(logger, String(propertyKey), e)
})

throw err
throw e
}

setImmediate(() => {
Expand Down
25 changes: 24 additions & 1 deletion src/main/core/manual-metric-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,35 @@
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { MetricReader } from '@opentelemetry/sdk-metrics'
import { type CollectionResult, type DataPoint, MetricReader } from '@opentelemetry/sdk-metrics'
import { type CommonReaderOptions } from '@opentelemetry/sdk-metrics/build/src/types.js'

interface UnlockedDataPoint extends DataPoint<number> {
startTime: DataPoint<number>['startTime']
endTime: DataPoint<number>['endTime']
}

/**
* A metric reader that can be invoked manually with `collect()` to obtain its metrics.
*/
export class ManualMetricReader extends MetricReader {
override async collect(options?: CommonReaderOptions | undefined): Promise<CollectionResult> {
const results = await super.collect(options)

// Zero out all startTime and EndTime attributes in data points
results.resourceMetrics.scopeMetrics.forEach((scopeMetric) => {
scopeMetric.metrics.forEach((metric) => {
metric.dataPoints.forEach((dataPoint) => {
const unlocked = dataPoint as UnlockedDataPoint
unlocked.startTime = [0, 0]
unlocked.endTime = [0, 0]
})
})
})

return results
}

protected override async onShutdown(): Promise<void> {
throw new Error('Method not implemented.')
}
Expand Down
9 changes: 6 additions & 3 deletions src/main/core/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ export abstract class Scope extends Loggable {
*/
public abstract run(): Promise<void>

private scope?: Meter
/**
* The metrics captured by this scope.
*/
public readonly metrics: Record<string, Counter>

private readonly metrics: Record<string, Counter>
private scope?: Meter

/**
* Instantiates a new scope.
Expand All @@ -46,7 +49,7 @@ export abstract class Scope extends Loggable {
*
* @param dataPoint - The actual metric data.
*/
protected capture(dataPoint: ScopeMetric): void {
public capture(dataPoint: ScopeMetric): void {
// Ensure a scope exists
if (this.scope === undefined) {
this.scope = opentelemetry.metrics.getMeter(this.name)
Expand Down
11 changes: 11 additions & 0 deletions src/main/exceptions/guard-shell-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright IBM Corp. 2023, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* Exception thrown when protecting exec commands from forbidden characters.
*/
export class GuardShellError extends Error {}
11 changes: 11 additions & 0 deletions src/main/exceptions/invalid-object-path-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright IBM Corp. 2023, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* Exception thrown when protecting exec commands from forbidden characters.
*/
export class InvalidObjectPathError extends Error {}
15 changes: 15 additions & 0 deletions src/main/exceptions/invalid-root-path-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright IBM Corp. 2023, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* Exception thrown when protecting exec commands from forbidden characters.
*/
export class InvalidRootPathError extends Error {
constructor(cwd: string, root: string) {
super(`${cwd} is not a subpath of ${root}`)
}
}
Loading