Skip to content

Commit

Permalink
Adds dependency module
Browse files Browse the repository at this point in the history
  • Loading branch information
isasmendiagus committed Oct 4, 2024
1 parent fe9bc6e commit 2ef19b8
Show file tree
Hide file tree
Showing 14 changed files with 691 additions and 284 deletions.
421 changes: 244 additions & 177 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"build:main": "tsc -p tsconfig.json",
"build:module": "tsc -p tsconfig.module.json",
"test": "nyc mocha -r ts-node/register 'tests/**/*.ts' 'src/**/*.spec.ts'",
"install-dev": "npm run build && npm run test && npm install -g ."
"install-dev": "npm run build && npm run test && npm install -g .",
"prepare": "npm run build"
},
"engines": {
"node": ">=10"
Expand Down Expand Up @@ -47,22 +48,26 @@
"devDependencies": {
"@ava/typescript": "^1.1.1",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/chai": "^4.3.1",
"@types/chai": "^4.3.20",
"@types/mocha": "^9.1.1",
"@types/mock-fs": "^4.13.4",
"@types/node": "^17.0.2",
"@types/node-fetch": "^2.6.2",
"@types/node-gzip": "^1.1.0",
"@types/sinon": "^17.0.3",
"@types/tar": "^6.1.3",
"@types/uuid": "^9.0.0",
"chai": "^4.3.6",
"chai": "^4.5.0",
"codecov": "^3.5.0",
"deep-equal-in-any-order": "^2.0.1",
"mocha": "^10.2.0",
"mocha": "^10.7.3",
"mock-fs": "^5.3.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"prettier": "^2.8.8",
"ts-node": "^10.9.1",
"typescript": "^5.5.4"
"sinon": "^19.0.2",
"ts-node": "^10.9.2",
"typescript": "^5.6.2"
},
"files": [
"build/main",
Expand Down
8 changes: 8 additions & 0 deletions src/api/Base.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import {
} from "./types";
import { Transport } from "./transport";

/**
* BaseApi class
*
* Base class for all API classes in the SCANOSS SDK.
* This class provides a common interface for all API classes to communicate with the SCANOSS server.
*
* @template S - The service key (must be a key of ScanossServices)
*/
export abstract class BaseApi<S extends keyof ScanossServices> {
protected constructor(protected t: Transport, protected serviceName: S) {}

Expand Down
3 changes: 3 additions & 0 deletions src/api/Cryptography.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { BaseApi } from "./Base.api";
import { Transport } from "./transport";
import { EchoRequest, EchoResponse } from "./types";

/**
* CryptographyApi encapsulates all cryptography-related operations in the SCANOSS SDK.
*/
export class CryptographyApi extends BaseApi<'cryptography'> {
constructor(t: Transport) {
super(t, 'cryptography');
Expand Down
1 change: 0 additions & 1 deletion src/api/transport/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// Define the Strategy interface
import { ExtractServiceMethod, ScanossServices } from "../types";

export interface Transport {
Expand Down
29 changes: 17 additions & 12 deletions src/api/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import { CryptographyService } from "./Cryptography";
import { DependencyService } from "./Dependency";

/**
* ScanossServices interface
*
* This interface defines the structure of all services provided by the SCANOSS SDK.
* It serves as a single source of truth for available services and their methods.
* Each property represents a specific service (e.g., cryptography, dependency) and
* maps to its corresponding service interface.
*/
export interface ScanossServices {
cryptography: CryptographyService;
dependency: DependencyService;
}


/**
* Common Request and Common Response Interfaces
Expand Down Expand Up @@ -76,6 +65,22 @@ export interface ServiceMethod<P = any, R = any> {
result: R;
}



/**
* ScanossServices interface
*
* This interface defines the structure of all services provided by the SCANOSS SDK.
* It serves as a single source of truth for available services and their methods.
* Each property represents a specific service (e.g., cryptography, dependency) and
* maps to its corresponding service interface.
*
* NOTE: Any new services added to the SDK should be added to this interface and create the api class for it.
*/
export interface ScanossServices {
cryptography: CryptographyService;
dependency: DependencyService;
}
// Export all types from common, cryptography, and dependency modules
export * from "./Cryptography";
export * from "./Dependency";
11 changes: 11 additions & 0 deletions src/errors/DependencyPurlExtractorNotSet.error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ScanossError } from "./Base.error";

/**
* Error thrown when an operation requiring a dependency purl extractor is attempted,
*
*/
export class DependencyPurlExtractorNotSetError extends ScanossError {
constructor(message: string = 'Dependency Purl Extractor Not Setted') {
super(message);
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ export { PurlRequest, EchoRequest } from './sdk/Services/Grpc/BaseService';
export * from './sdk/Services/http/HttpClient'

export { logger } from './sdk/Logger';

export * from './sdk/SCANOSS';
161 changes: 161 additions & 0 deletions src/modules/Dependency.module.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { expect } from 'chai';
import sinon from 'sinon';
import { DependencyModule, ExtractedComponent,DecoratedDependency, DependencyPURLExtractor } from './Dependency.module';
import { Transport } from '../api/transport';
import { DependencyApi, } from '../api/Dependency.api';
import { StatusCode } from '../api/types';
import * as fs from 'fs/promises';
let dependencyModule: DependencyModule;
let mockTransport: Transport;
let mockApi: sinon.SinonStubbedInstance<DependencyApi>;
let mockExtractor: DependencyPURLExtractor;
import mock from 'mock-fs';

describe('DependencyModule', () => {
let dependencyModule: DependencyModule;
let mockTransport: Transport;
let mockApi: sinon.SinonStubbedInstance<DependencyApi>;
let mockExtractor: DependencyPURLExtractor;

beforeEach(() => {
mockTransport = {
execute: sinon.stub(),
};

mockApi = sinon.createStubInstance(DependencyApi);

mockExtractor = {
run: sinon.stub(),
};

dependencyModule = new DependencyModule({transport: mockTransport, purlExtractor: mockExtractor});
(dependencyModule as any).api = mockApi;
});

afterEach(() => {
sinon.restore();
mock.restore();
});


describe('addFile', () => {
it('should add a file to the list', () => {
dependencyModule.addFile('test.js');
expect((dependencyModule as any).files).to.include('test.js');
});

it('should return this for chaining', () => {
const result = dependencyModule.addFile('test.js');
expect(result).to.equal(dependencyModule);
});
});

describe('addFiles', () => {
it('should add multiple files to the list', () => {
dependencyModule.addFiles(['test1.js', 'test2.js']);
expect((dependencyModule as any).files).to.include('test1.js');
expect((dependencyModule as any).files).to.include('test2.js');
});

it('should return this for chaining', () => {
const result = dependencyModule.addFiles(['test1.js', 'test2.js']);
expect(result).to.equal(dependencyModule);
});
});

describe('addComponent', () => {
it('should add a component to the list', () => {
const component: ExtractedComponent = { purl: 'pkg:npm/[email protected]', scope: 'runtime' };
dependencyModule.addComponent(component);
expect((dependencyModule as any).components).to.deep.include(component);
});

it('should return this for chaining', () => {
const component: ExtractedComponent = { purl: 'pkg:npm/[email protected]', scope: 'runtime' };
const result = dependencyModule.addComponent(component);
expect(result).to.equal(dependencyModule);
});
});

describe('extract', () => {
it('should extract dependencies from files and components', async () => {
const mockFileContent = 'mock content';
const mockExtractedDeps: ExtractedComponent[] = [
{ purl: 'pkg:npm/[email protected]', scope: 'runtime' },
];

mock({
'test.js': mockFileContent,
});

(mockExtractor.run as sinon.SinonStub).resolves(mockExtractedDeps);

dependencyModule.addFile('test.js');
dependencyModule.addComponent({ purl: 'pkg:npm/[email protected]', scope: 'runtime' });

const result = await dependencyModule.extract();

expect(result).to.deep.equal([
{ purl: 'pkg:npm/[email protected]', scope: 'runtime' },
...mockExtractedDeps,
]);

expect((mockExtractor.run as sinon.SinonStub).calledOnce).to.be.true;
expect((mockExtractor.run as sinon.SinonStub).firstCall.args[0]).to.deep.equal(Buffer.from(mockFileContent));
});
});

describe('decorate', () => {
it('should decorate dependencies', async () => {
const mockDependencies: ExtractedComponent[] = [
{ purl: 'pkg:npm/[email protected]', scope: 'runtime' },
];

const mockDecoratedDeps: DecoratedDependency[] = [{
file: 'dummy.txt',
id: '1',
status: 'success',
dependencies: [{
component: 'test',
purl: 'pkg:npm/[email protected]',
version: '1.0.0',
licenses: [],
url: 'https://example.com',
comment: '',
}],
}];

mockApi.get.resolves({
status: { status: StatusCode.SUCCESS, message: 'Success' },
files: mockDecoratedDeps,
});

const result = await dependencyModule.decorate(mockDependencies);

expect(result).to.deep.equal(mockDecoratedDeps);
expect(mockApi.get.calledOnce).to.be.true;
expect(mockApi.get.firstCall.args[0]).to.be.an('object');
});
});

describe('extractAndDecorate', () => {
});

describe('reset', () => {
it('should reset the internal state', () => {
dependencyModule.addFile('test.js');
dependencyModule.addComponent({ purl: 'pkg:npm/[email protected]', scope: 'runtime' });

dependencyModule.reset();

expect((dependencyModule as any).files).to.be.empty;
expect((dependencyModule as any).components).to.be.empty;
expect((dependencyModule as any).extractedDeps).to.be.empty;
});

it('should return this for chaining', () => {
const result = dependencyModule.reset();
expect(result).to.equal(dependencyModule);
});
});
});
Loading

0 comments on commit 2ef19b8

Please sign in to comment.