Skip to content

sdk-core, node: attribute validation #296

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

Merged
merged 4 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ export class ApplicationInformationAttributeProvider implements BacktraceAttribu
this._applicationVersion = this._applicationVersion ?? (applicationData['version'] as string);
}

if (!this._application || !this._applicationVersion) {
throw new Error(
'Cannot find information about the package. Please define application and application.version attribute',
);
}

return {
package: applicationData,
[this.APPLICATION_ATTRIBUTE]: this._application,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,5 @@ describe('Application information attribute provider tests', () => {

expect(attributes[provider.APPLICATION_VERSION_ATTRIBUTE]).toBe(expectedPackageJson.version);
});

it('Should throw an error when the package.json information does not exist', () => {
const testedPackageDir = path.join('/foo', 'bar', 'baz', '123', 'foo', 'bar');
const provider = new ApplicationInformationAttributeProvider([testedPackageDir], {});

expect(() => provider.get()).toThrow(Error);
});
});
});
26 changes: 26 additions & 0 deletions packages/sdk-core/src/BacktraceCoreClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ export abstract class BacktraceCoreClient<
}

public initialize() {
this.validateAttributes();

if (this.fileSystem && this.options.database?.createDatabaseDirectory) {
if (!this.options.database.path) {
throw new Error(
Expand Down Expand Up @@ -418,4 +420,28 @@ export abstract class BacktraceCoreClient<
private static destroy() {
this._instance = undefined;
}

private validateAttributes() {
function validateApplicationAndVersion(attributes: Record<string, unknown>) {
if (!attributes['application'] || !attributes['application.version']) {
return 'application and application.version attributes must be defined.';
}
}

// Validate scoped attributes first. If they return no errors, there is no point
// in checking all attributes, which resolving of may be slower.
const scoped = this.attributeManager.get('scoped');
const scopedError = validateApplicationAndVersion(scoped.attributes);
if (!scopedError) {
return;
}

const allAttributes = this.attributeManager.get();
const error = validateApplicationAndVersion(allAttributes.attributes);
if (!error) {
return;
}

throw new Error(error);
}
}
105 changes: 105 additions & 0 deletions packages/sdk-core/tests/client/clientTests.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { BacktraceReport, BacktraceStringAttachment } from '../../src/index.js';
import { AttributeManager } from '../../src/modules/attribute/AttributeManager.js';
import { BacktraceTestClient } from '../mocks/BacktraceTestClient.js';
import { testHttpClient } from '../mocks/testHttpClient.js';
describe('Client tests', () => {
afterEach(() => {
jest.restoreAllMocks();
});

describe('Send tests', () => {
const client = BacktraceTestClient.buildFakeClient();

Expand Down Expand Up @@ -128,4 +134,103 @@ describe('Client tests', () => {
);
});
});

describe('Validation tests', () => {
it('should throw on initialize when application and application.version attributes are missing', () => {
const instance = new BacktraceTestClient({}, testHttpClient);
expect(() => instance.initialize()).toThrow(
'application and application.version attributes must be defined.',
);
});

it('should throw on initialize when application attribute is missing', () => {
const instance = new BacktraceTestClient({}, testHttpClient, [
{
type: 'scoped',
get: () => ({
'application.version': '1.2.3',
}),
},
]);
expect(() => instance.initialize()).toThrow(
'application and application.version attributes must be defined.',
);
});

it('should throw on initialize when application.version attribute is missing', () => {
const instance = new BacktraceTestClient({}, testHttpClient, [
{
type: 'scoped',
get: () => ({
application: 'my-app',
}),
},
]);
expect(() => instance.initialize()).toThrow(
'application and application.version attributes must be defined.',
);
});

it('should not throw on initialize when application and application.version attributes are defined as scoped', () => {
const instance = new BacktraceTestClient({}, testHttpClient, [
{
type: 'scoped',
get: () => ({
application: 'my-app',
'application.version': '1.2.3',
}),
},
]);
expect(() => instance.initialize()).not.toThrow();
});

it('should not throw on initialize when application and application.version attributes are defined as dynamic', () => {
const instance = new BacktraceTestClient({}, testHttpClient, [
{
type: 'dynamic',
get: () => ({
application: 'my-app',
'application.version': '1.2.3',
}),
},
]);
expect(() => instance.initialize()).not.toThrow();
});

it('should only test scoped attributes and not all when application and application.version attributes are defined as scoped', () => {
const instance = new BacktraceTestClient({}, testHttpClient, [
{
type: 'scoped',
get: () => ({
application: 'my-app',
'application.version': '1.2.3',
}),
},
]);

const getAttributesSpy = jest.spyOn(AttributeManager.prototype, 'get');
instance.initialize();

expect(getAttributesSpy).toHaveBeenCalledWith('scoped');
expect(getAttributesSpy).not.toHaveBeenCalledWith();
});

it('should test both scoped attributes and all when application and application.version attributes are defined as dynamic', () => {
const instance = new BacktraceTestClient({}, testHttpClient, [
{
type: 'dynamic',
get: () => ({
application: 'my-app',
'application.version': '1.2.3',
}),
},
]);

const getAttributesSpy = jest.spyOn(AttributeManager.prototype, 'get');
instance.initialize();

expect(getAttributesSpy).toHaveBeenCalledWith('scoped');
expect(getAttributesSpy).toHaveBeenCalledWith();
});
});
});