mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-30 22:34:10 -05:00
refactor: relocate verdaccio-memory plugin (#1974)
This commit is contained in:
parent
1d11128051
commit
827376e85c
15 changed files with 1813 additions and 20 deletions
3
packages/plugins/memory/.babelrc
Normal file
3
packages/plugins/memory/.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../.babelrc"
|
||||||
|
}
|
5
packages/plugins/memory/jest.config.js
Normal file
5
packages/plugins/memory/jest.config.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const config = require('../../../jest/config');
|
||||||
|
|
||||||
|
module.exports = Object.assign({}, config, {
|
||||||
|
collectCoverage: true,
|
||||||
|
});
|
44
packages/plugins/memory/package.json
Normal file
44
packages/plugins/memory/package.json
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"name": "verdaccio-memory",
|
||||||
|
"version": "10.0.0-beta",
|
||||||
|
"description": "Storage implementation in memory",
|
||||||
|
"keywords": [
|
||||||
|
"verdaccio",
|
||||||
|
"plugin",
|
||||||
|
"storage",
|
||||||
|
"memory"
|
||||||
|
],
|
||||||
|
"author": "Juan Picado <juanpicado19@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://verdaccio.org",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/verdaccio/verdaccio",
|
||||||
|
"directory": "packages/plugins/memory"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/verdaccio/verdaccio/issues"
|
||||||
|
},
|
||||||
|
"main": "build/index.js",
|
||||||
|
"types": "build/index.d.ts",
|
||||||
|
"dependencies": {
|
||||||
|
"@verdaccio/commons-api": "workspace:*",
|
||||||
|
"@verdaccio/streams": "workspace:*",
|
||||||
|
"memory-fs": "^0.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@verdaccio/types": "workspace:*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf ./build",
|
||||||
|
"type-check": "tsc --noEmit -p tsconfig.build.json",
|
||||||
|
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
|
||||||
|
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
|
||||||
|
"build": "pnpm run build:js && pnpm run build:types",
|
||||||
|
"test": "cross-env NODE_ENV=test BABEL_ENV=test jest"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/verdaccio"
|
||||||
|
}
|
||||||
|
}
|
5
packages/plugins/memory/src/index.ts
Normal file
5
packages/plugins/memory/src/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import LocalMemory from './local-memory';
|
||||||
|
|
||||||
|
export { LocalMemory };
|
||||||
|
|
||||||
|
export default LocalMemory;
|
124
packages/plugins/memory/src/local-memory.ts
Normal file
124
packages/plugins/memory/src/local-memory.ts
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import { getServiceUnavailable } from '@verdaccio/commons-api';
|
||||||
|
import {
|
||||||
|
Logger,
|
||||||
|
Callback,
|
||||||
|
Config,
|
||||||
|
IPluginStorage,
|
||||||
|
Token,
|
||||||
|
TokenFilter,
|
||||||
|
PluginOptions,
|
||||||
|
} from '@verdaccio/types';
|
||||||
|
|
||||||
|
import MemoryHandler, { DataHandler } from './memory-handler';
|
||||||
|
|
||||||
|
export type ConfigMemory = Config & { limit?: number };
|
||||||
|
export interface MemoryLocalStorage {
|
||||||
|
secret: string;
|
||||||
|
list: string[];
|
||||||
|
files: DataHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_LIMIT = 1000;
|
||||||
|
class LocalMemory implements IPluginStorage<ConfigMemory> {
|
||||||
|
private path: string;
|
||||||
|
private limit: number;
|
||||||
|
public logger: Logger;
|
||||||
|
private data: MemoryLocalStorage;
|
||||||
|
public config: ConfigMemory;
|
||||||
|
|
||||||
|
public constructor(config: ConfigMemory, options: PluginOptions<ConfigMemory>) {
|
||||||
|
this.config = config;
|
||||||
|
this.limit = config.limit || DEFAULT_LIMIT;
|
||||||
|
this.logger = options.logger;
|
||||||
|
this.data = this._createEmtpyDatabase();
|
||||||
|
this.path = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSecret(): Promise<string> {
|
||||||
|
return Promise.resolve(this.data.secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSecret(secret: string): Promise<string | null> {
|
||||||
|
return new Promise((resolve): void => {
|
||||||
|
this.data.secret = secret;
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(name: string, cb: Callback): void {
|
||||||
|
const { list } = this.data;
|
||||||
|
|
||||||
|
if (list.length < this.limit) {
|
||||||
|
if (list.indexOf(name) === -1) {
|
||||||
|
list.push(name);
|
||||||
|
}
|
||||||
|
cb(null);
|
||||||
|
} else {
|
||||||
|
this.logger.info(
|
||||||
|
{ limit: this.limit },
|
||||||
|
'Storage memory has reached limit of @{limit} packages'
|
||||||
|
);
|
||||||
|
cb(new Error('Storage memory has reached limit of limit packages'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
public search(onPackage: Callback, onEnd: Callback, validateName: Function): void {
|
||||||
|
this.logger.warn('[verdaccio/memory]: search method not implemented, PR is welcome');
|
||||||
|
onEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(name: string, cb: Callback): void {
|
||||||
|
const { list } = this.data;
|
||||||
|
const item = list.indexOf(name);
|
||||||
|
|
||||||
|
if (item !== -1) {
|
||||||
|
list.splice(item, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(cb: Callback): void {
|
||||||
|
cb(null, this.data.list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPackageStorage(packageInfo: string): MemoryHandler {
|
||||||
|
return new MemoryHandler(packageInfo, this.data.files, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createEmtpyDatabase(): MemoryLocalStorage {
|
||||||
|
const list: string[] = [];
|
||||||
|
const files = {};
|
||||||
|
const emptyDatabase = {
|
||||||
|
list,
|
||||||
|
files,
|
||||||
|
secret: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return emptyDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveToken(token: Token): Promise<void> {
|
||||||
|
this.logger.warn('[verdaccio/memory][saveToken] save token has not been implemented yet');
|
||||||
|
|
||||||
|
return Promise.reject(getServiceUnavailable('method not implemented'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteToken(user: string, tokenKey: string): Promise<void> {
|
||||||
|
this.logger.warn(
|
||||||
|
{ tokenKey, user },
|
||||||
|
'[verdaccio/memory][deleteToken] delete token has not been implemented yet @{user}'
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.reject(getServiceUnavailable('method not implemented'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public readTokens(filter: TokenFilter): Promise<Token[]> {
|
||||||
|
this.logger.warn('[verdaccio/memory][readTokens] read tokens has not been implemented yet ');
|
||||||
|
|
||||||
|
return Promise.reject(getServiceUnavailable('method not implemented'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LocalMemory;
|
190
packages/plugins/memory/src/memory-handler.ts
Normal file
190
packages/plugins/memory/src/memory-handler.ts
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
import {
|
||||||
|
VerdaccioError,
|
||||||
|
getBadRequest,
|
||||||
|
getInternalError,
|
||||||
|
getConflict,
|
||||||
|
getNotFound,
|
||||||
|
} from '@verdaccio/commons-api';
|
||||||
|
import MemoryFileSystem from 'memory-fs';
|
||||||
|
import { UploadTarball, ReadTarball } from '@verdaccio/streams';
|
||||||
|
import {
|
||||||
|
Callback,
|
||||||
|
Logger,
|
||||||
|
IPackageStorageManager,
|
||||||
|
IUploadTarball,
|
||||||
|
IReadTarball,
|
||||||
|
CallbackAction,
|
||||||
|
StorageUpdateCallback,
|
||||||
|
StorageWriteCallback,
|
||||||
|
PackageTransformer,
|
||||||
|
Package,
|
||||||
|
ReadPackageCallback,
|
||||||
|
} from '@verdaccio/types';
|
||||||
|
|
||||||
|
import { parsePackage, stringifyPackage } from './utils';
|
||||||
|
|
||||||
|
const fs = new MemoryFileSystem();
|
||||||
|
|
||||||
|
export type DataHandler = {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryHandler implements IPackageStorageManager {
|
||||||
|
private data: DataHandler;
|
||||||
|
private name: string;
|
||||||
|
private path: string;
|
||||||
|
public logger: Logger;
|
||||||
|
|
||||||
|
public constructor(packageName: string, data: DataHandler, logger: Logger) {
|
||||||
|
// this is not need it
|
||||||
|
this.data = data;
|
||||||
|
this.name = packageName;
|
||||||
|
this.logger = logger;
|
||||||
|
this.path = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public updatePackage(
|
||||||
|
pkgFileName: string,
|
||||||
|
updateHandler: StorageUpdateCallback,
|
||||||
|
onWrite: StorageWriteCallback,
|
||||||
|
transformPackage: PackageTransformer,
|
||||||
|
onEnd: CallbackAction
|
||||||
|
): void {
|
||||||
|
const json: string = this._getStorage(pkgFileName);
|
||||||
|
let pkg: Package;
|
||||||
|
|
||||||
|
try {
|
||||||
|
pkg = parsePackage(json) as Package;
|
||||||
|
} catch (err) {
|
||||||
|
return onEnd(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHandler(pkg, (err: VerdaccioError) => {
|
||||||
|
if (err) {
|
||||||
|
return onEnd(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
onWrite(pkgFileName, transformPackage(pkg), onEnd);
|
||||||
|
} catch (err) {
|
||||||
|
return onEnd(getInternalError('error on parse the metadata'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePackage(pkgName: string, callback: Callback): void {
|
||||||
|
delete this.data[pkgName];
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removePackage(callback: CallbackAction): void {
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createPackage(name: string, value: Package, cb: CallbackAction): void {
|
||||||
|
this.savePackage(name, value, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public savePackage(name: string, value: Package, cb: CallbackAction): void {
|
||||||
|
try {
|
||||||
|
const json: string = stringifyPackage(value);
|
||||||
|
|
||||||
|
this.data[name] = json;
|
||||||
|
return cb(null);
|
||||||
|
} catch (err) {
|
||||||
|
return cb(getInternalError(err.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readPackage(name: string, cb: ReadPackageCallback): void {
|
||||||
|
const json = this._getStorage(name);
|
||||||
|
const isJson = typeof json === 'undefined';
|
||||||
|
|
||||||
|
try {
|
||||||
|
return cb(isJson ? getNotFound() : null, parsePackage(json));
|
||||||
|
} catch (err) {
|
||||||
|
return cb(getNotFound());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeTarball(name: string): IUploadTarball {
|
||||||
|
const uploadStream: IUploadTarball = new UploadTarball({});
|
||||||
|
const temporalName = `/${name}`;
|
||||||
|
|
||||||
|
process.nextTick(function () {
|
||||||
|
fs.stat(temporalName, function (fileError, stats) {
|
||||||
|
if (!fileError && stats) {
|
||||||
|
return uploadStream.emit('error', getConflict());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const file = fs.createWriteStream(temporalName);
|
||||||
|
|
||||||
|
uploadStream.pipe(file);
|
||||||
|
|
||||||
|
uploadStream.done = function (): void {
|
||||||
|
const onEnd = function (): void {
|
||||||
|
uploadStream.emit('success');
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadStream.on('end', onEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadStream.abort = function (): void {
|
||||||
|
uploadStream.emit('error', getBadRequest('transmision aborted'));
|
||||||
|
file.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadStream.emit('open');
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
uploadStream.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return uploadStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readTarball(name: string): IReadTarball {
|
||||||
|
const pathName = `/${name}`;
|
||||||
|
|
||||||
|
const readTarballStream: IReadTarball = new ReadTarball({});
|
||||||
|
|
||||||
|
process.nextTick(function () {
|
||||||
|
fs.stat(pathName, function (fileError, stats) {
|
||||||
|
if (fileError && !stats) {
|
||||||
|
return readTarballStream.emit('error', getNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const readStream = fs.createReadStream(pathName);
|
||||||
|
|
||||||
|
const contentLength: number = (fs.data[name] && fs.data[name].length) || 0;
|
||||||
|
readTarballStream.emit('content-length', contentLength);
|
||||||
|
readTarballStream.emit('open');
|
||||||
|
readStream.pipe(readTarballStream);
|
||||||
|
readStream.on('error', (error: VerdaccioError) => {
|
||||||
|
readTarballStream.emit('error', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
readTarballStream.abort = function (): void {
|
||||||
|
readStream.destroy(getBadRequest('read has been aborted'));
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
readTarballStream.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return readTarballStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getStorage(name = ''): string {
|
||||||
|
return this.data[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MemoryHandler;
|
9
packages/plugins/memory/src/utils.ts
Normal file
9
packages/plugins/memory/src/utils.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { Package } from '@verdaccio/types';
|
||||||
|
|
||||||
|
export function stringifyPackage(pkg: Package) {
|
||||||
|
return JSON.stringify(pkg, null, '\t');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsePackage(pkg: string) {
|
||||||
|
return JSON.parse(pkg);
|
||||||
|
}
|
75
packages/plugins/memory/test/local-memory.spec.ts
Normal file
75
packages/plugins/memory/test/local-memory.spec.ts
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import { Logger, IPluginStorage } from '@verdaccio/types';
|
||||||
|
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||||
|
|
||||||
|
import { ConfigMemory } from '../src/local-memory';
|
||||||
|
import { DataHandler } from '../src/memory-handler';
|
||||||
|
import LocalMemory from '../src/index';
|
||||||
|
|
||||||
|
import config from './partials/config';
|
||||||
|
|
||||||
|
const logger: Logger = {
|
||||||
|
error: (e) => console.warn(e),
|
||||||
|
info: (e) => console.warn(e),
|
||||||
|
debug: (e) => console.warn(e),
|
||||||
|
child: (e) => console.warn(e),
|
||||||
|
warn: (e) => console.warn(e),
|
||||||
|
http: (e) => console.warn(e),
|
||||||
|
trace: (e) => console.warn(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultConfig = { logger, config: null };
|
||||||
|
|
||||||
|
describe('memory unit test .', () => {
|
||||||
|
describe('LocalMemory', () => {
|
||||||
|
test('should create an LocalMemory instance', () => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
|
||||||
|
expect(localMemory).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create add a package', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
localMemory.add('test', (err: VerdaccioError) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
localMemory.get((err: VerdaccioError, data: DataHandler) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(data).toHaveLength(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should reach max limit', (done) => {
|
||||||
|
config.limit = 2;
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
|
||||||
|
localMemory.add('test1', (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
localMemory.add('test2', (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
localMemory.add('test3', (err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err.message).toMatch(/Storage memory has reached limit of limit packages/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should remove a package', (done) => {
|
||||||
|
const pkgName = 'test';
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
localMemory.add(pkgName, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
localMemory.remove(pkgName, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
localMemory.get((err, data) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(data).toHaveLength(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
380
packages/plugins/memory/test/memory.spec.ts
Normal file
380
packages/plugins/memory/test/memory.spec.ts
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
import { Logger, IPluginStorage, IPackageStorage, ILocalPackageManager } from '@verdaccio/types';
|
||||||
|
import { getInternalError } from '@verdaccio/commons-api';
|
||||||
|
|
||||||
|
import { ConfigMemory } from '../src/local-memory';
|
||||||
|
import MemoryHandler from '../src/memory-handler';
|
||||||
|
import LocalMemory from '../src/index';
|
||||||
|
|
||||||
|
import config from './partials/config';
|
||||||
|
import pkgExample from './partials/pkg';
|
||||||
|
|
||||||
|
const logger: Logger = {
|
||||||
|
error: (e) => console.warn(e),
|
||||||
|
info: (e) => console.warn(e),
|
||||||
|
debug: (e) => console.warn(e),
|
||||||
|
child: (e) => console.warn(e),
|
||||||
|
warn: (e) => console.warn(e),
|
||||||
|
http: (e) => console.warn(e),
|
||||||
|
trace: (e) => console.warn(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultConfig = { logger, config: null };
|
||||||
|
|
||||||
|
const mockStringify = jest.fn((value) => {
|
||||||
|
return jest.requireActual('../src/utils.ts').stringifyPackage(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockParsePackage = jest.fn((value) => {
|
||||||
|
return jest.requireActual('../src/utils.ts').parsePackage(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('../src/utils.ts', () => ({
|
||||||
|
stringifyPackage: (value) => mockStringify(value),
|
||||||
|
parsePackage: (value) => mockParsePackage(value),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('memory unit test .', () => {
|
||||||
|
describe('MemoryHandler', () => {
|
||||||
|
test('should create an MemoryHandler instance', () => {
|
||||||
|
const memoryHandler = new MemoryHandler(
|
||||||
|
'test',
|
||||||
|
{
|
||||||
|
['foo']: 'bar',
|
||||||
|
},
|
||||||
|
logger
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(memoryHandler).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should save a package', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
handler.readPackage(pkgName, (err, data) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(data).toEqual(pkgExample);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should fails on save a package', (done) => {
|
||||||
|
mockStringify.mockImplementationOnce(() => {
|
||||||
|
throw new Error('error on parse');
|
||||||
|
});
|
||||||
|
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test';
|
||||||
|
|
||||||
|
const handler: IPackageStorage = localMemory.getPackageStorage(
|
||||||
|
pkgName
|
||||||
|
) as ILocalPackageManager;
|
||||||
|
|
||||||
|
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||||
|
expect(err).toEqual(getInternalError('error on parse'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should fails on read a package', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler.readPackage(pkgName, (err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err.code).toBe(404);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should update a package', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
const onEnd = jest.fn();
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
|
||||||
|
handler.updatePackage(
|
||||||
|
pkgName,
|
||||||
|
(json, callback) => {
|
||||||
|
expect(json).toBeDefined();
|
||||||
|
expect(json.name).toBe(pkgExample.name);
|
||||||
|
expect(callback).toBeDefined();
|
||||||
|
callback(null);
|
||||||
|
},
|
||||||
|
(name, data, onEnd) => {
|
||||||
|
expect(name).toBe(pkgName);
|
||||||
|
expect(data.name).toBe(pkgExample.name);
|
||||||
|
onEnd();
|
||||||
|
expect(onEnd).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
(data) => {
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
onEnd
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should parse fails on update a package', (done) => {
|
||||||
|
mockParsePackage.mockImplementationOnce(() => {
|
||||||
|
throw new Error('error on parse');
|
||||||
|
});
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
|
||||||
|
const pkgName = 'test';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
const onEnd = jest.fn((err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err).toEqual(getInternalError('error on parse'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
handler.updatePackage(
|
||||||
|
pkgName,
|
||||||
|
() => {},
|
||||||
|
() => {},
|
||||||
|
// @ts-ignore
|
||||||
|
() => {},
|
||||||
|
onEnd
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should fail updateHandler update a package', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
const onEnd = jest.fn((err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err).toEqual(getInternalError('some error'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
|
||||||
|
handler.updatePackage(
|
||||||
|
pkgName,
|
||||||
|
(json, callback) => {
|
||||||
|
expect(json).toBeDefined();
|
||||||
|
expect(json.name).toBe(pkgExample.name);
|
||||||
|
expect(callback).toBeDefined();
|
||||||
|
callback(getInternalError('some error'));
|
||||||
|
},
|
||||||
|
() => {},
|
||||||
|
// @ts-ignore
|
||||||
|
() => {},
|
||||||
|
onEnd
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should onWrite update a package', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
const onEnd = jest.fn((err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err).toEqual(getInternalError('error on parse the metadata'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler.savePackage(pkgName, pkgExample, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
|
||||||
|
handler.updatePackage(
|
||||||
|
pkgName,
|
||||||
|
(json, callback) => {
|
||||||
|
expect(json).toBeDefined();
|
||||||
|
expect(json.name).toBe(pkgExample.name);
|
||||||
|
expect(callback).toBeDefined();
|
||||||
|
callback(null);
|
||||||
|
},
|
||||||
|
(name, data, onEnd) => {
|
||||||
|
expect(name).toBe(pkgName);
|
||||||
|
expect(data.name).toBe(pkgExample.name);
|
||||||
|
onEnd();
|
||||||
|
expect(onEnd).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
throw new Error('dadsads');
|
||||||
|
},
|
||||||
|
onEnd
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('writing/reading files', () => {
|
||||||
|
test('should write a tarball', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test';
|
||||||
|
const dataTarball = '12345';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
const stream = handler.writeTarball(pkgName);
|
||||||
|
stream.on('data', (data) => {
|
||||||
|
expect(data.toString()).toBe(dataTarball);
|
||||||
|
});
|
||||||
|
stream.on('open', () => {
|
||||||
|
stream.done();
|
||||||
|
stream.end();
|
||||||
|
});
|
||||||
|
stream.on('success', () => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.write(dataTarball);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should read a tarball', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test.tar.gz';
|
||||||
|
const dataTarball = '12345';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
const stream = handler.writeTarball(pkgName);
|
||||||
|
stream.on('open', () => {
|
||||||
|
stream.done();
|
||||||
|
stream.end();
|
||||||
|
});
|
||||||
|
stream.on('success', () => {
|
||||||
|
const readStream = handler.readTarball(pkgName);
|
||||||
|
readStream.on('data', (data) => {
|
||||||
|
expect(data.toString()).toBe(dataTarball);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
stream.write(dataTarball);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should abort read a tarball', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test2.tar.gz';
|
||||||
|
const dataTarball = '12345';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
const stream = handler.writeTarball(pkgName);
|
||||||
|
stream.on('open', () => {
|
||||||
|
stream.done();
|
||||||
|
stream.end();
|
||||||
|
});
|
||||||
|
stream.on('success', () => {
|
||||||
|
const readStream = handler.readTarball(pkgName);
|
||||||
|
readStream.on('data', () => {
|
||||||
|
readStream.abort();
|
||||||
|
});
|
||||||
|
readStream.on('error', (err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err.message).toMatch(/read has been aborted/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
stream.write(dataTarball);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should fails read a tarball not found', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test2.tar.gz';
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
const readStream = handler.readTarball('not-found');
|
||||||
|
readStream.on('error', (err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err.message).toMatch(/no such package/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should abort while write a tarball', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test-abort.tar.gz';
|
||||||
|
const dataTarball = '12345';
|
||||||
|
|
||||||
|
const handler = localMemory.getPackageStorage(pkgName);
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
const stream = handler.writeTarball(pkgName);
|
||||||
|
stream.on('error', (err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err.message).toMatch(/transmision aborted/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
stream.on('open', () => {
|
||||||
|
stream.abort();
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.write(dataTarball);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should delete a package', (done) => {
|
||||||
|
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
|
||||||
|
const pkgName = 'test2';
|
||||||
|
|
||||||
|
const handler: IPackageStorage = localMemory.getPackageStorage(pkgName);
|
||||||
|
expect(handler).toBeDefined();
|
||||||
|
if (handler) {
|
||||||
|
handler.createPackage(pkgName, pkgExample, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
handler.deletePackage(pkgName, (err) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
handler.readPackage(pkgName, (err) => {
|
||||||
|
expect(err).not.toBeNull();
|
||||||
|
expect(err.message).toMatch(/no such package/);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
50
packages/plugins/memory/test/partials/config.ts
Normal file
50
packages/plugins/memory/test/partials/config.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { Config } from '@verdaccio/types';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
user_agent: 'string',
|
||||||
|
server_id: 1234,
|
||||||
|
secret: '12345',
|
||||||
|
self_path: './nowhere',
|
||||||
|
uplinks: {
|
||||||
|
npmjs: {
|
||||||
|
url: 'https://registry.npmjs.org/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
web: {
|
||||||
|
sign: {},
|
||||||
|
verify: {},
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
legacy: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
packages: {
|
||||||
|
test: {
|
||||||
|
storage: '',
|
||||||
|
publish: [''],
|
||||||
|
proxy: [''],
|
||||||
|
access: [''],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
enable: true,
|
||||||
|
title: 'string',
|
||||||
|
logo: 'string',
|
||||||
|
},
|
||||||
|
logs: [],
|
||||||
|
auth: {},
|
||||||
|
notifications: {
|
||||||
|
method: '',
|
||||||
|
packagePattern: /a/,
|
||||||
|
packagePatternFlags: '',
|
||||||
|
headers: {},
|
||||||
|
endpoint: '',
|
||||||
|
content: '',
|
||||||
|
},
|
||||||
|
checkSecretKey: () => '1234',
|
||||||
|
getMatchedPackagesSpec: jest.fn(),
|
||||||
|
hasProxyTo: () => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
65
packages/plugins/memory/test/partials/pkg.ts
Normal file
65
packages/plugins/memory/test/partials/pkg.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { Package } from '@verdaccio/types';
|
||||||
|
|
||||||
|
const pkg: Package = {
|
||||||
|
name: '@scope/test_npm',
|
||||||
|
versions: {
|
||||||
|
'1.0.1': {
|
||||||
|
name: '@scope/test_npm',
|
||||||
|
version: '1.0.1',
|
||||||
|
description: '',
|
||||||
|
main: 'index.js',
|
||||||
|
readme: 'test readme',
|
||||||
|
scripts: {
|
||||||
|
test: "echo 'Error: no test specified' && exit 1",
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
name: 'Juan Picado',
|
||||||
|
email: 'me@jotadeveloper.com',
|
||||||
|
url: 'http://jotadeveloper.com/',
|
||||||
|
},
|
||||||
|
license: 'ISC',
|
||||||
|
dependencies: {
|
||||||
|
'create-react-app': '^1.4.1',
|
||||||
|
'fast-static-site': '^1.0.2',
|
||||||
|
watchdom: '^1.0.2',
|
||||||
|
},
|
||||||
|
_id: '@scope/test_npm@1.0.1',
|
||||||
|
_npmUser: {
|
||||||
|
name: 'jpicado',
|
||||||
|
email: 'dsa@dasd.com',
|
||||||
|
},
|
||||||
|
maintainers: [
|
||||||
|
{
|
||||||
|
name: 'jpicado',
|
||||||
|
email: 'dsa@dasd.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dist: {
|
||||||
|
integrity:
|
||||||
|
'sha512-0ThGF2zZiOGmLoHl/n5cMwAS6swbAz7rdzDjgkyDh+C2rADzNfPIfo7KBTRHbY6uJ9akBCvWDFBuR0fgaxYnjQ==',
|
||||||
|
shasum: '1df0c3dfd289b2ac6ef00b0129cab9737eeaa62d',
|
||||||
|
tarball: 'http://localhost:4873/@scope/test_npm/-/@scope/test_npm-1.0.1.tgz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'dist-tags': {
|
||||||
|
latest: '1.0.1',
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
modified: '2018-02-20T17:50:47.944Z',
|
||||||
|
created: '2018-02-20T17:50:47.944Z',
|
||||||
|
'1.0.1': '2018-02-20T17:50:47.944Z',
|
||||||
|
},
|
||||||
|
_distfiles: {},
|
||||||
|
_attachments: {
|
||||||
|
'test_npm-1.0.1.tgz': {
|
||||||
|
shasum: '1df0c3dfd289b2ac6ef00b0129cab9737eeaa62d',
|
||||||
|
version: '1.0.1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_uplinks: {},
|
||||||
|
_rev: '5-ea87644a96a129cf',
|
||||||
|
readme: 'ERROR: No README data found!',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default pkg;
|
9
packages/plugins/memory/tsconfig.build.json
Normal file
9
packages/plugins/memory/tsconfig.build.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./build"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"exclude": ["src/**/*.test.ts"]
|
||||||
|
}
|
20
packages/plugins/memory/tsconfig.json
Normal file
20
packages/plugins/memory/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.reference.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./build"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "types/*.d.ts"],
|
||||||
|
"exclude": ["src/**/*.test.ts"],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "../../core/commons-api"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../../core/streams"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../../core/types"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
853
pnpm-lock.yaml
853
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
||||||
packages:
|
packages:
|
||||||
- packages/*
|
- packages/*
|
||||||
- packages/core/*
|
- packages/core/*
|
||||||
|
- packages/plugins/*
|
||||||
- website
|
- website
|
||||||
- '!**/test/**'
|
- '!**/test/**'
|
||||||
|
|
Loading…
Reference in a new issue