Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@elastic.io/maester-client

Package Overview
Dependencies
Maintainers
12
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@elastic.io/maester-client - npm Package Compare versions

Comparing version 3.4.4-dev.2 to 4.0.0-dev.1

.env.example

65

.eslintrc.js

@@ -7,38 +7,51 @@ module.exports = {

},
extends: ["airbnb-base"],
extends: [
'airbnb-base',
],
globals: {
Atomics: "readonly",
SharedArrayBuffer: "readonly",
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
parser: "@typescript-eslint/parser",
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: "module",
sourceType: 'module',
},
plugins: ["@typescript-eslint"],
plugins: [
'@typescript-eslint',
],
rules: {
quotes: ["error", "single"],
"no-plusplus": 0,
"no-await-in-loop": 0,
"prefer-default-export": 0,
"import/extensions": 0,
"import/prefer-default-export": 0,
"class-methods-use-this": 0,
"import/no-unresolved": 0,
"no-unused-vars": 0,
"max-len": ["error", { code: 180 }],
"no-param-reassign": 0,
"guard-for-in": "off",
"no-return-assign": 0,
"no-prototype-builtins": 0,
"operator-linebreak": 0,
'linebreak-style': 0,
'import/no-extraneous-dependencies': ['error', { devDependencies: ['spec/**/*', 'spec-integration/**/*'] }],
'no-plusplus': 0,
'no-unused-vars': 0,
'no-await-in-loop': 0,
'prefer-default-export': 0,
'import/prefer-default-export': 0,
'class-methods-use-this': 1,
'max-len': ['error', { code: 180 }],
'no-param-reassign': 1,
'no-return-assign': 1,
'no-use-before-define': 0,
'comma-dangle': 0,
'object-curly-newline': 0,
camelcase: 0,
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
},
settings: {
"import/extensions": [".js", ".jsx", ".ts", ".tsx"],
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"],
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
"import/resolver": {
'import/resolver': {
node: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},

@@ -45,0 +58,0 @@ },

@@ -1,4 +0,7 @@

*
# 4.0.0 (June 17, 2022)
* new version of library
# 3.4.3 (April 8, 2022)
* Fix dependencies
# 3.4.2 (July 27, 2021)

@@ -5,0 +8,0 @@ * Update headers validation

40

package.json
{
"name": "@elastic.io/maester-client",
"version": "3.4.4-dev.2",
"version": "4.0.0-dev.1",
"description": "The official object-storage client",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"tsc": "rm -fr dist && tsc",
"lint": "eslint '*/**/*.ts' --quiet --fix",
"test": "NODE_ENV=test mocha -r ts-node/register --recursive ./spec/**/*.ts",
"pretest": "npm run lint && find src spec -name \"*.js\" -type f -delete",
"lint": "eslint '*/**/*[^.d$].ts' --quiet --fix",
"test": "NODE_ENV=test mocha -r ts-node/register --recursive spec/**/*.ts --timeout 10000",
"integration-test": "npm run pretest && mocha --exit --r ts-node/register spec-integration/**/*.ts --timeout 1000000",
"pretest": "eslint --ext .ts --quiet --fix && find src spec spec-integration -name \"*.js\" -type f -delete && find src spec spec-integration -name \"*.d.ts\" -type f -delete",
"posttest": "npm run tsc",

@@ -35,19 +36,27 @@ "build": "npm run tsc",

"devDependencies": {
"@elastic.io/component-logger": "0.0.1",
"@types/chai": "4.2.11",
"@types/chai-as-promised": "7.1.5",
"@types/dicer": "0.2.0",
"@types/jsonwebtoken": "8.3.9",
"@types/lodash": "4.14.170",
"@types/mocha": "9.1.0",
"@types/mocha": "9.0.0",
"@types/nock": "11.1.0",
"@types/node": "16.9.6",
"@types/qs": "6.9.1",
"@typescript-eslint/eslint-plugin": "2.28.0",
"@typescript-eslint/parser": "2.28.0",
"chai": "4.2.0",
"eslint": "6.8.0",
"eslint-config-airbnb-base": "14.0.0",
"eslint-plugin-import": "2.20.1",
"mocha": "9.2.2",
"chai": "4.3.4",
"chai-as-promised": "7.1.1",
"dotenv": "16.0.1",
"eslint": "7.32.0",
"eslint-config-airbnb-base": "14.2.1",
"eslint-plugin-import": "2.24.2",
"is-uuid": "1.0.2",
"mocha": "10.0.0",
"nock": "12.0.3",
"ts-node": "8.8.2",
"typescript": "3.8.3"
"nyc": "15.1.0",
"sinon": "11.1.2",
"ts-node": "10.2.1",
"typescript": "4.4.3"
},

@@ -57,6 +66,9 @@ "dependencies": {

"@types/sinon": "10.0.0",
"aws-sdk": "2.1152.0",
"axios": "0.26.1",
"form-data": "4.0.0",
"get-stream": "6.0.1",
"jsonwebtoken": "8.5.1",
"sinon": "10.0.0"
}
}

@@ -0,5 +1,15 @@

/* eslint-disable import/first */
process.env.REQUEST_MAX_RETRY = '3';
process.env.REQUEST_RETRY_DELAY = '0';
import { Readable, Duplex } from 'stream';
import * as crypto from 'crypto';
import * as zlib from 'zlib';
import getLogger from '@elastic.io/component-logger';
import sinon from 'sinon';
export const getContext = () => ({
logger: getLogger(),
emit: sinon.spy(),
});
const MESSAGE_CRYPTO_PASSWORD = 'testCryptoPassword';

@@ -9,9 +19,2 @@ const MESSAGE_CRYPTO_IV = 'iv=any16_symbols';

export const streamResponse = (responseData: any) => () => {
const stream = new Readable();
stream.push(JSON.stringify(responseData));
stream.push(null);
return stream;
};
export const encryptStream = (): Duplex => {

@@ -30,1 +33,9 @@ const encodeKey = crypto.createHash('sha256').update(MESSAGE_CRYPTO_PASSWORD, 'utf8').digest();

export const unzip = (): Duplex => zlib.createGunzip();
export const streamFromObject = (data: object): Readable => {
const dataString = JSON.stringify(data);
const stream = new Readable();
stream.push(dataString);
stream.push(null);
return stream;
};
/* eslint-disable no-unused-expressions */
import nock from 'nock';
import sinonjs, { SinonSandbox } from 'sinon';
import sinon from 'sinon';
import getStream from 'get-stream';
import { expect } from 'chai';
import { ObjectStorage, StorageClient } from '../src';
import {
describe, beforeEach, afterEach, it,
} from 'mocha';
import { Readable } from 'stream';
import { ObjectStorage } from '../src/ObjectStorage';
import logging from '../src/logger';
import {
streamResponse, encryptStream, decryptStream, zip, unzip,
encryptStream, decryptStream, zip, unzip, streamFromObject
} from './helpers';
const formStream = (dataString: string): Readable => {
const stream = new Readable();
stream.push(dataString);
stream.push(null);
return stream;
};
describe('Object Storage', () => {

@@ -28,5 +16,13 @@ const config = {

};
const objectStorage = new ObjectStorage(config);
const postData = { test: 'test' };
const createdObjWithQueryField = {
contentType: 'application/json',
createdAt: 1622811501107,
objectId: '2bd48165-119f-489d-8842-8d07b2c7cc1b',
metadata: {},
queriableFields: {
demosearchfield: 'qwerty',
},
};
const responseData = {

@@ -40,106 +36,103 @@ contentLength: 'meta.contentLength',

};
// eslint-disable-next-line max-len
const responseString = '{"contentLength":"meta.contentLength","contentType":"meta.contentType","createdAt":"meta.createdAt","md5":"meta.md5Hash","objectId":"obj.id","metadata":"meta.userMetadata"}';
let sinon: SinonSandbox;
beforeEach(async () => {
sinon = sinonjs.createSandbox();
});
afterEach(() => {
sinon.restore();
});
let finalReqCfg;
afterEach(sinon.restore);
describe('basic', () => {
describe('data mode', () => {
it('should getAllByParams', async () => {
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects?foo=bar')
.reply(200, {});
await objectStorage.getAllByParams({ foo: 'bar' });
expect(objectStorageCalls.isDone()).to.be.true;
describe('should getAllByParams', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => (
{ data: streamFromObject([createdObjWithQueryField, createdObjWithQueryField]) }
));
});
it('should getAllByParams', async () => {
const result = await objectStorage.getAllByParams({ foo: 'bar' });
expect(JSON.parse(result)).to.deep.equal([createdObjWithQueryField, createdObjWithQueryField]);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.deep.equal({
method: 'get',
url: '/objects',
responseType: 'stream',
params: { foo: 'bar' },
headers: { Authorization: 'Bearer jwt' }
});
});
});
it('should getById (stream)', async () => {
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/objectId')
.reply(200, formStream('i`m a stream'));
const result = await objectStorage.getById('objectId', 'stream');
expect(result.toString('base64')).to.be.equal(formStream('i`m a stream').toString());
expect(objectStorageCalls.isDone()).to.be.true;
describe('should getById (stream)', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: streamFromObject({ q: 'i`m a stream' }) }));
});
it('should getById (stream)', async () => {
const result = await objectStorage.getOne('objectId', { responseType: 'stream' });
const streamAsJSON = await getStream(result);
expect(JSON.parse(streamAsJSON)).to.be.deep.equal({ q: 'i`m a stream' });
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.deep.equal({
method: 'get',
url: '/objects/objectId',
responseType: 'stream',
params: {},
headers: { Authorization: 'Bearer jwt' }
});
});
});
it('should getById (json)', async () => {
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/objectId')
.reply(200, formStream('i`m a stream'));
const result = await objectStorage.getById('objectId', 'json');
expect(result).to.be.deep.equal('i`m a stream');
expect(objectStorageCalls.isDone()).to.be.true;
describe('should getById (json)', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: streamFromObject({ q: 'i`m a stream' }) }));
});
it('should getById (json)', async () => {
const result = await objectStorage.getOne('objectId', { responseType: 'json' });
expect(result).to.be.equal(JSON.stringify({ q: 'i`m a stream' }));
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.deep.equal({
method: 'get',
url: '/objects/objectId',
responseType: 'stream',
params: {},
headers: { Authorization: 'Bearer jwt' }
});
});
});
it('should getById (arraybuffer)', async () => {
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/objectId')
.reply(200, formStream('i`m a stream'));
const result = await objectStorage.getById('objectId', 'arraybuffer');
const encodedResult = Buffer.from('i`m a stream', 'binary').toString('base64');
expect(result.toString('base64')).to.be.equal(encodedResult);
expect(objectStorageCalls.isDone()).to.be.true;
describe('should getById (arraybuffer)', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: streamFromObject({ q: 'i`m a stream' }) }));
});
it('should getById (arraybuffer)', async () => {
const result = await objectStorage.getOne('objectId', { responseType: 'arraybuffer' });
const encodedResult = Buffer.from(JSON.stringify({ q: 'i`m a stream' }), 'binary').toString('base64');
expect(result.toString('base64')).to.be.equal(encodedResult);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.deep.equal({
method: 'get',
url: '/objects/objectId',
responseType: 'stream',
params: {},
headers: { Authorization: 'Bearer jwt' }
});
});
});
});
describe('stream mode', () => {
it('should fail after 3 get retries', async () => {
const log = sinon.stub(logging, 'warn');
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/1')
.replyWithError({ code: 'ETIMEDOUT' })
.get('/objects/1')
.reply(404)
.get('/objects/1')
.replyWithError({ code: 'ENOTFOUND' });
.times(3)
.reply(500);
let err;
try {
await objectStorage.getById('1');
} catch (e) {
err = e;
}
await expect(objectStorage.getOne('1')).to.be.rejectedWith('Server error during request');
expect(objectStorageCalls.isDone()).to.be.true;
expect(err.code).to.be.equal('ENOTFOUND');
expect(log.getCall(1).args[1].toString()).to.include('404');
expect(log.callCount).to.be.equal(2);
});
it('should retry get request on errors', async () => {
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)

@@ -149,62 +142,32 @@ .get('/objects/1')

.get('/objects/1')
.reply(200, streamResponse(responseData));
.reply(200, streamFromObject(responseData));
const response = await objectStorage.getById('1');
const response = await objectStorage.getOne('1', { responseType: 'json' });
expect(objectStorageCalls.isDone()).to.be.true;
expect(response).to.be.deep.equal(responseString);
expect(response).to.be.deep.equal(JSON.stringify(responseData));
});
it('should throw an error on post request connection error', async () => {
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.replyWithError({ code: 'ECONNREFUSED' })
.post('/objects')
.replyWithError({ code: 'ECONNREFUSED' })
.post('/objects')
.times(3)
.replyWithError({ code: 'ECONNREFUSED' });
let err;
try {
await objectStorage.postObject(postData, {});
} catch (e) {
err = e;
}
await expect(objectStorage.add(postData, {})).to.be.rejectedWith('Server error during request');
expect(objectStorageCalls.isDone()).to.be.true;
expect(err.code).to.be.equal('ECONNREFUSED');
});
it('should throw an error on post request http error', async () => {
const objectStorage = new ObjectStorage(config);
it('should throw an error immediately on post request http error', async () => {
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.reply(409)
.post('/objects')
.reply(409)
.post('/objects')
.reply(409);
let err;
try {
await objectStorage.postObject(postData, {});
} catch (e) {
err = e;
}
await expect(objectStorage.add(postData, {})).to.be.rejectedWith('Request failed with status code 409');
expect(objectStorageCalls.isDone()).to.be.true;
expect(err.toString()).to.include('409');
});
it('should post successfully', async () => {
const objectStorage = new ObjectStorage(config);
const objectStorageCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)

@@ -214,10 +177,8 @@ .post('/objects')

const response: any = await objectStorage.postObject(postData, {});
const objectId = await objectStorage.add(postData, {});
expect(objectStorageCalls.isDone()).to.be.true;
expect(response.objectId).to.match(/^[0-9a-z-]+$/);
expect(objectId).to.match(/^[0-9a-z-]+$/);
});
});
});
describe('middlewares + zip/unzip and encrypt/decrypt', () => {

@@ -229,26 +190,11 @@ describe('stream mode', () => {

objectStorageWithMiddlewares.use(zip, unzip);
const log = sinon.stub(logging, 'warn');
const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/1')
.replyWithError({ code: 'ETIMEDOUT' })
.get('/objects/1')
.reply(404)
.get('/objects/1')
.replyWithError({ code: 'ENOTFOUND' });
.times(3)
.replyWithError({ code: 'ETIMEDOUT' });
let err;
try {
await objectStorageWithMiddlewares.getById('1');
} catch (e) {
err = e;
}
await expect(objectStorageWithMiddlewares.getOne('1')).to.be.rejectedWith('Server error during request');
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(err.code).to.be.equal('ENOTFOUND');
expect(log.getCall(1).args[1].toString()).to.include('404');
expect(log.callCount).to.be.equal(2);
});
it('should retry get request on errors', async () => {

@@ -258,4 +204,4 @@ const objectStorageWithMiddlewares = new ObjectStorage(config);

objectStorageWithMiddlewares.use(zip, unzip);
const responseStream = streamFromObject(responseData).pipe(encryptStream()).pipe(zip());
const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)

@@ -265,13 +211,9 @@ .get('/objects/1')

.get('/objects/1')
.reply(200, () => {
const stream = streamResponse(responseData)();
return stream.pipe(encryptStream()).pipe(zip());
});
.reply(200, responseStream);
const response = await objectStorageWithMiddlewares.getById('1');
const stream = await objectStorageWithMiddlewares.getOne('1', { responseType: 'stream' });
const result = await getStream(stream);
expect(result).to.be.deep.equal(JSON.stringify(responseData));
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(response).to.be.deep.equal(responseString);
});
it('should throw an error on post request connection error', async () => {

@@ -282,22 +224,10 @@ const objectStorageWithMiddlewares = new ObjectStorage(config);

const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.replyWithError({ code: 'ECONNREFUSED' })
.post('/objects')
.replyWithError({ code: 'ECONNREFUSED' })
.post('/objects')
.times(3)
.replyWithError({ code: 'ECONNREFUSED' });
let err;
try {
await objectStorageWithMiddlewares.postObject(postData, {});
} catch (e) {
err = e;
}
await expect(objectStorageWithMiddlewares.add(postData, {})).to.be.rejectedWith('Server error during request');
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(err.code).to.be.equal('ECONNREFUSED');
});
it('should throw an error on post request http error', async () => {

@@ -308,21 +238,9 @@ const objectStorageWithMiddlewares = new ObjectStorage(config);

const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.reply(409)
.post('/objects')
.reply(409)
.post('/objects')
.reply(409);
let err;
try {
await objectStorageWithMiddlewares.postObject(postData, {});
} catch (e) {
err = e;
}
await expect(objectStorageWithMiddlewares.add(postData, {})).to.be.rejectedWith('Request failed with status code 409');
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(err.toString()).to.include('409');
});
it('should post successfully', async () => {

@@ -333,72 +251,45 @@ const objectStorageWithMiddlewares = new ObjectStorage(config);

const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.reply(200, { objectId: '1' });
.reply(200, streamFromObject({ objectId: 'dfsf-2dasd3-dsf2l' }));
const response:any = await objectStorageWithMiddlewares.postObject(postData, {});
const response = await objectStorageWithMiddlewares.add(postData, {});
expect(response).to.be.equal('dfsf-2dasd3-dsf2l');
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(response.objectId).to.be.equal('1');
});
it('should add 2 objects successfully', async () => {
const objectStorageWithMiddlewares = new ObjectStorage(config);
objectStorageWithMiddlewares.use(encryptStream, decryptStream);
objectStorageWithMiddlewares.use(zip, unzip);
const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.reply(200, { objectId: '1' })
.post('/objects')
.reply(200, { objectId: '2' });
const response1: any = await objectStorageWithMiddlewares.postObject(postData, {});
const response2: any = await objectStorageWithMiddlewares.postObject(postData, {});
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(response1.objectId).to.be.equal('1');
expect(response2.objectId).to.be.equal('2');
});
});
describe('configure ReqOptions', () => {
describe('configure ReqOptions', () => {
beforeEach(async () => {
finalReqCfg = sinon.spy(StorageClient.prototype, <any>'requestRetry');
});
it('should get 2 objects successfully', async () => {
const objectStorageWithMiddlewares = new ObjectStorage(config);
objectStorageWithMiddlewares.use(encryptStream, decryptStream);
objectStorageWithMiddlewares.use(zip, unzip);
const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
it('configure ReqOptions', async () => {
const objectStorageCalls = nock(config.uri)
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/1')
.reply(200, () => {
const stream = streamResponse(responseData)();
return stream.pipe(encryptStream()).pipe(zip());
})
.get('/objects/2')
.reply(200, () => {
const stream = streamResponse(responseData)();
return stream.pipe(encryptStream()).pipe(zip());
});
.times(5)
.replyWithError({ code: 'ETIMEDOUT' });
const outStreamFirst = await objectStorageWithMiddlewares.getById('1');
const outStreamSecond = await objectStorageWithMiddlewares.getById('2');
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(outStreamFirst).to.be.deep.equal(responseString);
expect(outStreamSecond).to.be.deep.equal(responseString);
const retryOptions = { retriesCount: 5, requestTimeout: 1, retryDelay: 1 };
await expect(objectStorage.getOne('1', { retryOptions })).to.be.rejectedWith('Server error during request');
expect(objectStorageCalls.isDone()).to.be.true;
const { lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal(retryOptions);
});
it('should use valid jwt token', async () => {
const objectStorageWithMiddlewares = new ObjectStorage(config);
objectStorageWithMiddlewares.use(encryptStream, decryptStream);
objectStorageWithMiddlewares.use(zip, unzip);
const objectStorageWithMiddlewaresCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
it('configure ReqOptions', async () => {
const objectStorageCalls = nock(config.uri)
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.reply(200);
.get('/objects/1')
.times(4)
.replyWithError({ code: 'ETIMEDOUT' })
.get('/objects/1')
.reply(200, streamFromObject({ objectId: '234-sdf' }));
const response:any = await objectStorageWithMiddlewares.postObject(postData, {});
expect(objectStorageWithMiddlewaresCalls.isDone()).to.be.true;
expect(response.objectId).to.match(/^[0-9a-z-]+$/);
const retryOptions = { retriesCount: 5, requestTimeout: 1, retryDelay: 1 };
const result = await objectStorage.getOne('1', { retryOptions });
expect(JSON.parse(result)).to.be.deep.equal({ objectId: '234-sdf' });
expect(objectStorageCalls.isDone()).to.be.true;
const { lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal(retryOptions);
});

@@ -405,0 +296,0 @@ });

/* eslint-disable max-len */
import 'mocha';
import chai from 'chai';
import nock from 'nock';
import chai, { expect } from 'chai';
import sinon from 'sinon';
import bunyan from '@elastic.io/bunyan-logger';
import { ObjectStorageWrapper, MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS } from '../src/ObjectStorageWrapper';
import getStream from 'get-stream';
import chaiAsPromised from 'chai-as-promised';
import { getContext, streamFromObject } from './helpers';
import { ObjectStorageWrapper, StorageClient, MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS } from '../src';
const { expect } = chai;
chai.use(chaiAsPromised);
process.env.ELASTICIO_OBJECT_STORAGE_TOKEN =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRJZCI6IjU2YzIwN2FkYjkxMjExODFlNjUwYzBlZiIsImNvbnRyYWN0SWQiOiI1YjVlZDFjZjI3MmNmODAwMTFhZTdiNmEiLCJ3b3Jrc3BhY2VJZCI6IjVhNzFiZmM1NjA3ZjFiMDAwNzI5OGEyYSIsImZsb3dJZCI6IioiLCJ1c2VySWQiOiI1YjE2NGRiMzRkNTlhODAwMDdiZDQ3OTMiLCJpYXQiOjE1ODg1ODg3NjZ9.3GlJAwHz__e2Y5tgkzD1t-JyhgXGJOSVFSLUBCqLh5Y';
process.env.ELASTICIO_WORKSPACE_ID = 'test';
process.env.ELASTICIO_FLOW_ID = 'test';
process.env.ELASTICIO_API_URI = 'https://api.hostname';
process.env.ELASTICIO_OBJECT_STORAGE_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6I';
process.env.ELASTICIO_OBJECT_STORAGE_URI = 'https://ma.estr';
process.env.ELASTICIO_STEP_ID = 'step_id';
let context: any;
let objectStorageWrapper: any;
describe('ObjectStorageWrapper', () => {
const objectStorageWrapper = new ObjectStorageWrapper(getContext());
const genHeaders = (amount: number) => {

@@ -37,8 +31,6 @@ const resultHeaders = [];

};
const rawData = '{"foo":"bar"}';
const queryKey = 'baz';
const queryValue = 'bap';
const ttl = -1;
const ttl = 10;
const id = 'id123';
const maesterUri = 'https://ma.estr';
const createObjectWithQueriableField = {

@@ -53,154 +45,117 @@ contentType: 'application/json',

};
const anotherCreateObjectWithQueriableField = {
contentType: 'application/json',
createdAt: 1622811501108,
objectId: '78asdas87-ss77-77ss-7888-8d07b2c7cc2a',
metadata: {},
queriableFields: {
demosearchfield: 'qwerty',
},
};
before(async () => {
context = {
logger: bunyan.createLogger({ name: 'dummy' }),
emit: sinon.spy(),
};
objectStorageWrapper = new ObjectStorageWrapper(context);
});
let finalReqCfg;
beforeEach(async () => {
context.emit.resetHistory();
});
afterEach(sinon.restore);
after(() => {
nock.restore();
nock.cleanAll();
nock.activate();
});
describe('Create object', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: createObjectWithQueriableField }));
});
describe('valid inputs', () => {
describe('With queriable and meta fields', () => {
it('Should save the data correctly', async () => {
nock(maesterUri)
.post('/objects')
.matchHeader('x-query-key0', 'value0')
.matchHeader('x-eio-ttl', '-1')
.reply(201, createObjectWithQueriableField);
await objectStorageWrapper.createObject(data, genHeaders(1), [], ttl);
it('Should save the data correctly (1 q-header, ttl-value)', async () => {
const result = await objectStorageWrapper.createObject(data, genHeaders(1), [], ttl);
expect(result).to.be.equal(createObjectWithQueriableField.objectId);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
const stream = await firstArg.getFreshStream();
expect(await getStream(stream)).to.be.equal(JSON.stringify(data));
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'post',
url: '/objects',
headers: {
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I',
'content-type': 'application/json',
'x-query-key0': 'value0',
'x-eio-ttl': '10'
}
});
it('Should save the data correctly', async () => {
nock(maesterUri)
.post('/objects')
.reply(201, createObjectWithQueriableField);
await objectStorageWrapper.createObject(data);
});
it('Should save the data correctly (2 q-header, 3 q-header)', async () => {
const result = await objectStorageWrapper.createObject(data, genHeaders(2), genHeaders(3));
expect(result).to.be.equal(createObjectWithQueriableField.objectId);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
const stream = await firstArg.getFreshStream();
expect(await getStream(stream)).to.be.equal(JSON.stringify(data));
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'post',
url: '/objects',
headers: {
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I',
'content-type': 'application/json',
'x-query-key0': 'value0',
'x-query-key1': 'value1',
'x-meta-key0': 'value0',
'x-meta-key1': 'value1',
'x-meta-key2': 'value2',
}
});
it('Should save the data correctly', async () => {
nock(maesterUri)
.post('/objects')
.reply(201, createObjectWithQueriableField);
await objectStorageWrapper.createObject(data, []);
});
it('Should save the data correctly (no headers)', async () => {
const result = await objectStorageWrapper.createObject(data);
expect(result).to.be.equal(createObjectWithQueriableField.objectId);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
const stream = await firstArg.getFreshStream();
expect(await getStream(stream)).to.be.equal(JSON.stringify(data));
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'post',
url: '/objects',
headers: {
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I',
'content-type': 'application/json',
}
});
it('Should save the data correctly', async () => {
nock(maesterUri)
.post('/objects')
.reply(201, createObjectWithQueriableField);
await objectStorageWrapper.createObject(data, null);
});
it('Should save the data correctly', async () => {
nock(maesterUri)
.post('/objects')
.matchHeader('x-query-key0', 'value0')
.matchHeader('x-query-key1', 'value1')
.matchHeader('x-query-key2', 'value2')
.matchHeader('x-query-key3', 'value3')
.matchHeader('x-query-key4', 'value4')
.matchHeader('x-meta-key0', 'value0')
.matchHeader('x-meta-key1', 'value1')
.matchHeader('x-eio-ttl', '-1')
.reply(201, createObjectWithQueriableField);
await objectStorageWrapper.createObject(data, genHeaders(MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS), genHeaders(2), ttl);
});
});
describe('Without queriable fields', () => {
it('Should save the data correctly', async () => {
nock(maesterUri).post('/objects').matchHeader('x-eio-ttl', '-1').reply(201, createObjectWithQueriableField);
await objectStorageWrapper.createObject(data, genHeaders(1), [], ttl);
it('Should save the data correctly (no headers)', async () => {
const result = await objectStorageWrapper.createObject(data, [], []);
expect(result).to.be.equal(createObjectWithQueriableField.objectId);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
const stream = await firstArg.getFreshStream();
expect(await getStream(stream)).to.be.equal(JSON.stringify(data));
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'post',
url: '/objects',
headers: {
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I',
'content-type': 'application/json',
}
});
it('Should save the data correctly', async () => {
nock(maesterUri).post('/objects').reply(201, createObjectWithQueriableField);
await objectStorageWrapper.createObject(data);
});
});
});
describe('invalid inputs', () => {
describe('Query key set, query value undefined', () => {
it('Should throw error', async () => {
await objectStorageWrapper
.createObject(data, [{ key: 'key0', value: 'value0' }, { key: 'key1' }], [], ttl)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header "value" is mandatory if header "key" passed');
});
});
it('Should throw error', async () => {
await objectStorageWrapper
.createObject(data, genHeaders(1), [{ key: 'key0', value: 'value0' }, { key: 'key1' }], ttl)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header "value" is mandatory if header "key" passed');
});
});
it('Should throw error', async () => {
await expect(
// @ts-ignore
objectStorageWrapper.createObject(data, [{ key: 'key0', value: 'value0' }, { key: 'key1' }], [], ttl)
).to.be.rejectedWith('header "value" is mandatory if header "key" passed');
});
describe('Query value set, query key undefined', () => {
it('Should throw error', async () => {
await objectStorageWrapper.createObject(data, [{ value: 'value1' }], [], ttl).catch((error: { message: any }) => {
expect(error.message).to.equal('header "key" is mandatory if header "value" passed');
});
});
it('Should throw error', async () => {
await objectStorageWrapper.createObject(data, genHeaders(1), [{ value: 'value1' }], ttl).catch((error: { message: any }) => {
expect(error.message).to.equal('header "key" is mandatory if header "value" passed');
});
});
it('Should throw error', async () => {
await expect(
// @ts-ignore
objectStorageWrapper.createObject(data, [{ value: 'value1' }], [], ttl)
).to.be.rejectedWith('header "key" is mandatory if header "value" passed');
});
describe(`Maester headers maximum amount is exceed (${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS} items)`, () => {
it('Should throw error', async () => {
await objectStorageWrapper
.createObject(data, genHeaders(MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS + 1), [], ttl)
.catch((error: { message: any }) => {
expect(error.message).to.equal(`maximum available amount of headers is ${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS}`);
});
});
it(`Should throw error, Maester headers maximum amount is exceed (${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS} items)`, async () => {
await expect(
// @ts-ignore
objectStorageWrapper.createObject(data, genHeaders(MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS + 1), [], ttl)
).to.be.rejectedWith(`maximum available amount of headers is ${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS}`);
});
describe('Header used more than one time', () => {
it('Should throw error', async () => {
await objectStorageWrapper
.createObject(
data,
[
{ key: 'key0', value: 'value0' },
{ key: 'key0', value: 'value0' },
],
[],
ttl,
)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header key "key0" was already added');
});
});
it('Should throw error', async () => {
await objectStorageWrapper
.createObject(
data,
genHeaders(1),
[
{ key: 'key0', value: 'value0' },
{ key: 'key0', value: 'value0' },
],
ttl,
)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header key "key0" was already added');
});
});
it('Should throw error, Header used more than one time', async () => {
await expect(
// @ts-ignore
objectStorageWrapper.createObject(
data,
[
{ key: 'key0', value: 'value0' },
{ key: 'key1', value: 'value0' },
{ key: 'key0', value: 'value0' },
],
[],
ttl,
)
).to.be.rejectedWith('header key "key0" was already added');
});

@@ -210,155 +165,95 @@ });

describe('Lookup object by ID', () => {
describe('Lookup with valid ID', () => {
it('Should successfully return a JSON object', async () => {
nock(maesterUri).get(`/objects/${id}`).reply(200, data);
const result = await objectStorageWrapper.lookupObjectById(id);
expect(result).to.deep.equal(rawData);
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: streamFromObject(data) }));
});
it('Should successfully return object', async () => {
const result = await objectStorageWrapper.lookupObjectById(id);
expect(JSON.parse(result)).to.be.deep.equal(data);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.deep.equal({
method: 'get',
url: '/objects/id123',
responseType: 'stream',
params: {},
headers: { Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I' }
});
it('Should successfully return a string', async () => {
nock(maesterUri).get(`/objects/${id}`).reply(200, rawData);
const result = await objectStorageWrapper.lookupObjectById(id);
expect(result).to.deep.equal(rawData);
});
});
});
describe('Lookup objects by query parameters', () => {
describe('Objects not found in Maester', () => {
it('Should successfully return an empty stringified array', async () => {
nock(maesterUri).get(`/objects?query[${queryKey}]=${queryValue}`).reply(200, []);
const result = await objectStorageWrapper.lookupObjectsByQueryParameters([{ key: queryKey, value: queryValue }]);
expect(result).to.deep.equal([]);
});
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({
data: streamFromObject([createObjectWithQueriableField, createObjectWithQueriableField])
}));
});
describe('Different amount of search params', () => {
describe('valid input', () => {
it('Should successfully return an empty stringified array', async () => {
nock(maesterUri).get('/objects?query[key0]=value0').reply(200, []);
const result = await objectStorageWrapper.lookupObjectsByQueryParameters(genHeaders(1));
expect(result).to.deep.equal([]);
});
it('Should successfully return an empty stringified array', async () => {
nock(maesterUri)
.get('/objects?query[key0]=value0&query[key1]=value1&query[key2]=value2&query[key3]=value3&query[key4]=value4')
.reply(200, []);
const result = await objectStorageWrapper.lookupObjectsByQueryParameters(genHeaders(5));
expect(result).to.deep.equal([]);
});
describe('One object found in Maester', () => {
it('Should return a stringified array of 1 object', async () => {
nock(maesterUri).get('/objects?query[key0]=value0&query[key1]=value1').reply(200, [createObjectWithQueriableField]);
const result = await objectStorageWrapper.lookupObjectsByQueryParameters(genHeaders(2));
expect(result).to.deep.equal([createObjectWithQueriableField]);
});
});
describe('Two objects found in Maester', () => {
it('Should return a stringified array of 2 objects', async () => {
nock(maesterUri)
.get('/objects?query[key0]=value0&query[key1]=value1&query[key2]=value2')
.reply(200, [createObjectWithQueriableField, anotherCreateObjectWithQueriableField]);
const result = await objectStorageWrapper.lookupObjectsByQueryParameters(genHeaders(3));
expect(result).to.deep.equal([createObjectWithQueriableField, anotherCreateObjectWithQueriableField]);
});
});
it('Should return 2 objects', async () => {
const result = await objectStorageWrapper.lookupObjectsByQueryParameters([{ key: queryKey, value: queryValue }]);
expect(JSON.parse(result)).to.deep.equal([createObjectWithQueriableField, createObjectWithQueriableField]);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.deep.equal({
method: 'get',
url: '/objects',
responseType: 'stream',
params: { 'query[baz]': 'bap' },
headers: { Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I' }
});
describe('invalid input', () => {
describe('Query key set, query value undefined', () => {
it('Should throw error', async () => {
await objectStorageWrapper
.lookupObjectsByQueryParameters([{ key: 'key0', value: 'value0' }, { key: 'key1' }])
.catch((error: { message: any }) => {
expect(error.message).to.equal('header "value" is mandatory if header "key" passed');
});
});
});
describe('Query value set, query key undefined', () => {
it('Should throw error', async () => {
await objectStorageWrapper.lookupObjectsByQueryParameters([{ value: 'value1' }]).catch((error: { message: any }) => {
expect(error.message).to.equal('header "key" is mandatory if header "value" passed');
});
});
});
describe(`Maester headers maximum amount is exceed (${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS} items)`, () => {
it('Should throw error', async () => {
await objectStorageWrapper
.lookupObjectsByQueryParameters(genHeaders(MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS + 1))
.catch((error: { message: any }) => {
expect(error.message).to.equal(`maximum available amount of headers is ${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS}`);
});
});
});
describe('Header used more than one time', () => {
it('Should throw error', async () => {
await objectStorageWrapper
.lookupObjectsByQueryParameters(
[
{ key: 'key0', value: 'value0' },
{ key: 'key0', value: 'value0' },
],
)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header key "key0" was already added');
});
});
});
it('At least one header must be present', async () => {
try {
await objectStorageWrapper.lookupObjectsByQueryParameters([]);
} catch (error) {
expect(error.message).to.be.equal('At least one query header must be present');
}
});
});
});
it('Should throw error: query key set, query value undefined', async () => {
await expect(
// @ts-ignore
objectStorageWrapper.lookupObjectsByQueryParameters([{ key: 'key0', value: 'value0' }, { key: 'key1' }])
).to.be.rejectedWith('header "value" is mandatory if header "key" passed');
});
});
describe('Update object', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: createObjectWithQueriableField }));
});
describe('Valid update request', () => {
it('Should successfully update an object', async () => {
nock(maesterUri).put(`/objects/${id}`).reply(200, updatedData);
const result = await objectStorageWrapper.updateObject(id, updatedData);
expect(result).to.deep.equal(updatedData);
const result = await objectStorageWrapper.updateObjectById(id, updatedData);
expect(result).to.deep.equal(createObjectWithQueriableField);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
const stream = await firstArg.getFreshStream();
expect(await getStream(stream)).to.be.equal(JSON.stringify(updatedData));
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'put',
url: '/objects/id123',
headers: {
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I',
'content-type': 'application/json'
}
});
});
it('Should successfully update an object', async () => {
nock(maesterUri).put(`/objects/${id}`).reply(200, updatedData);
const result = await objectStorageWrapper.updateObject(id, updatedData, []);
expect(result).to.deep.equal(updatedData);
});
it('Should successfully update an object', async () => {
nock(maesterUri).put(`/objects/${id}`).reply(200, updatedData);
const result = await objectStorageWrapper.updateObject(id, updatedData, null);
expect(result).to.deep.equal(updatedData);
});
it('Should successfully update an object with headers', async () => {
nock(maesterUri)
.put(`/objects/${id}`)
.matchHeader('x-query-key0', 'value0')
.reply(200, updatedData);
const result = await objectStorageWrapper.updateObject(id, updatedData, genHeaders(1));
expect(result).to.deep.equal(updatedData);
const result = await objectStorageWrapper.updateObjectById(id, updatedData, genHeaders(3), genHeaders(2));
expect(result).to.deep.equal(createObjectWithQueriableField);
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
const stream = await firstArg.getFreshStream();
expect(await getStream(stream)).to.be.equal(JSON.stringify(updatedData));
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'put',
url: '/objects/id123',
headers: {
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I',
'content-type': 'application/json',
'x-query-key0': 'value0',
'x-query-key1': 'value1',
'x-query-key2': 'value2',
'x-meta-key0': 'value0',
'x-meta-key1': 'value1'
}
});
});
it('Should successfully update an object with headers', async () => {
nock(maesterUri).put(`/objects/${id}`)
.matchHeader('x-query-key0', 'value0')
.matchHeader('x-query-key1', 'value1')
.matchHeader('x-query-key2', 'value2')
.matchHeader('x-query-key3', 'value3')
.matchHeader('x-query-key4', 'value4')
.reply(200, updatedData);
const result = await objectStorageWrapper.updateObject(id, updatedData, genHeaders(5));
expect(result).to.deep.equal(updatedData);
});
it('Should successfully update an object with headers', async () => {
nock(maesterUri).put(`/objects/${id}`)
.matchHeader('x-query-key0', 'value0')
.matchHeader('x-query-key1', 'value1')
.matchHeader('x-meta-key0', 'value0')
.matchHeader('x-meta-key1', 'value1')
.reply(200, updatedData);
const result = await objectStorageWrapper.updateObject(id, updatedData, genHeaders(5), genHeaders(2));
expect(result).to.deep.equal(updatedData);
});
});
describe('Invalid update request', () => {
it('Should throw an error', async () => {
await objectStorageWrapper
.updateObject(
await expect(
// @ts-ignore
objectStorageWrapper.updateObjectById(
id,

@@ -371,5 +266,3 @@ updatedData,

)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header key "key0" was already added');
});
).to.be.rejectedWith('header key "key0" was already added');
});

@@ -379,6 +272,16 @@ });

describe('Delete object by ID', () => {
describe('ID is valid', () => {
it('Should delete an object', async () => {
nock(maesterUri).delete(`/objects/${id}`).reply(204);
await objectStorageWrapper.deleteObjectById(id);
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: '' }));
});
it('should delete object by ID', async () => {
const result = await objectStorageWrapper.deleteObjectById(id);
expect(result.data).to.be.equal('');
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'delete',
url: '/objects/id123',
params: {},
headers: { Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I' }
});

@@ -388,62 +291,20 @@ });

describe('Delete object by query parameter', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: '' }));
});
describe('Different amount of search params', () => {
describe('valid input', () => {
it('Should successfully delete objects (one param)', async () => {
nock(maesterUri).delete('/objects?query[key0]=value0').reply(200);
await objectStorageWrapper.deleteObjectsByQueryParameters(genHeaders(1));
});
it('Should successfully delete objects (two params)', async () => {
nock(maesterUri)
.delete('/objects?query[key0]=value0&query[key1]=value1&query[key2]=value2')
.reply(200, [createObjectWithQueriableField, anotherCreateObjectWithQueriableField]);
await objectStorageWrapper.deleteObjectsByQueryParameters(genHeaders(3));
});
it('Should successfully delete objects (five params)', async () => {
nock(maesterUri)
.delete('/objects?query[key0]=value0&query[key1]=value1&query[key2]=value2&query[key3]=value3&query[key4]=value4')
.reply(200, []);
await objectStorageWrapper.deleteObjectsByQueryParameters(genHeaders(5));
});
});
describe('invalid input', () => {
describe('Query key set, query value undefined', () => {
it('Should throw error', async () => {
await objectStorageWrapper
.deleteObjectsByQueryParameters([{ key: 'key0', value: 'value0' }, { key: 'key1' }], ttl)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header "value" is mandatory if header "key" passed');
});
const result = await objectStorageWrapper.deleteObjectsByQueryParameters(genHeaders(2));
expect(result.data).to.be.equal('');
const { firstArg, lastArg } = finalReqCfg.getCall(0);
expect(lastArg).to.be.deep.equal({});
expect(firstArg.getFreshStream).to.be.equal(undefined);
expect(firstArg.axiosReqConfig).to.be.deep.equal({
method: 'delete',
url: '/objects',
params: { 'query[key0]': 'value0', 'query[key1]': 'value1' },
headers: { Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I' }
});
});
describe('Query value set, query key undefined', () => {
it('Should throw error', async () => {
await objectStorageWrapper.deleteObjectsByQueryParameters([{ value: 'value1' }], ttl).catch((error: { message: any }) => {
expect(error.message).to.equal('header "key" is mandatory if header "value" passed');
});
});
});
describe(`Maester headers maximum amount is exceed (${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS} items)`, () => {
it('Should throw error', async () => {
await objectStorageWrapper
.deleteObjectsByQueryParameters(genHeaders(MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS + 1), ttl)
.catch((error: { message: any }) => {
expect(error.message).to.equal(`maximum available amount of headers is ${MAESTER_MAX_SUPPORTED_COUNT_OF_QUERY_HEADERS}`);
});
});
});
describe('Header used more than one time', () => {
it('Should throw error', async () => {
await objectStorageWrapper
.deleteObjectsByQueryParameters(
[
{ key: 'key0', value: 'value0' },
{ key: 'key0', value: 'value0' },
],
ttl,
)
.catch((error: { message: any }) => {
expect(error.message).to.equal('header key "key0" was already added');
});
});
});
});

@@ -450,0 +311,0 @@ });

/* eslint-disable no-unused-expressions */
import nock from 'nock';
import sinonjs, { SinonSandbox } from 'sinon';
import sinon from 'sinon';
import { expect } from 'chai';
import {
describe, beforeEach, afterEach, it,
} from 'mocha';
import { Readable } from 'stream';
import getStream from 'get-stream';
import { StorageClient } from '../src/StorageClient';
import logging from '../src/logger';
import { streamResponse } from './helpers';
import { streamFromObject } from './helpers';

@@ -19,5 +15,4 @@ describe('Storage Client', () => {

};
const storageClient = new StorageClient(config);
const data = { test: 'test' };
const responseData = {

@@ -31,51 +26,14 @@ contentLength: 'meta.contentLength',

};
let putStream: () => Readable;
let sinon: SinonSandbox;
beforeEach(async () => {
sinon = sinonjs.createSandbox();
putStream = () => {
const stream = new Readable();
stream.push(JSON.stringify(data));
stream.push(null);
return stream;
};
});
afterEach(() => {
sinon.restore();
});
it('should fail after 3 retries', async () => {
const log = sinon.stub(logging, 'warn');
const storageClient = new StorageClient(config);
const storageClientCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/1')
.replyWithError({ code: 'ETIMEDOUT' })
.get('/objects/1')
.reply(404)
.get('/objects/1')
.replyWithError({ code: 'ENOTFOUND' });
.times(3)
.reply(520);
let err;
try {
await storageClient.readStream('1');
} catch (e) {
err = e;
}
await expect(storageClient.get('1', {})).to.be.rejectedWith('Server error during request');
expect(storageClientCalls.isDone()).to.be.true;
expect(err.code).to.be.equal('ENOTFOUND');
expect(log.getCall(1).args[1].toString()).to.include('404');
expect(log.callCount).to.be.equal(2);
});
it('should retry get request 3 times on errors', async () => {
const storageClient = new StorageClient(config);
const storageClientCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)

@@ -87,17 +45,11 @@ .get('/objects/1')

.get('/objects/1')
.reply(200, streamResponse(data));
.reply(200, streamFromObject(data));
const response = await storageClient.readStream('1');
const { data: stream } = await storageClient.get('1', {});
const response = await getStream(stream);
expect(JSON.parse(response)).to.be.deep.equal(data);
expect(storageClientCalls.isDone()).to.be.true;
expect(response.data).to.be.instanceOf(Readable);
const resultData = JSON.parse(await getStream(response.data));
expect(resultData).to.be.deep.equal(data);
});
it('should retry post request 3 times on errors', async () => {
const storageClient = new StorageClient(config);
const storageClientCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)

@@ -107,41 +59,29 @@ .post('/objects')

.post('/objects')
.reply(400)
.reply(505)
.post('/objects')
.reply(200, responseData);
const response = await storageClient.writeStream(putStream, {});
const response = await storageClient.post(streamFromObject.bind({}, data));
expect(response.data).to.be.deep.equal(responseData);
expect(storageClientCalls.isDone()).to.be.true;
});
it('should accept jwt token on add', async () => {
const storageClient = new StorageClient(config);
it('INSURE IT`S A FRESH STREAM ON EACH RETRY', async () => {
const log = sinon.stub(logging, 'debug');
const storageClientCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.post('/objects')
.times(2)
.reply(500)
.post('/objects')
.reply(200, responseData);
const response = await storageClient.writeStream(putStream, {});
expect(storageClientCalls.isDone()).to.be.true;
const response = await storageClient.post(streamFromObject.bind({}, data), {});
expect(response.data).to.be.deep.equal(responseData);
expect(storageClientCalls.isDone()).to.be.true;
const firstStreamInstance = log.getCall(0).args[0];
const secStreamInstance = log.getCall(1).args[0];
const thirdStreamInstance = log.getCall(2).args[0];
expect(firstStreamInstance === secStreamInstance).to.be.equal(false);
expect(secStreamInstance === thirdStreamInstance).to.be.equal(false);
});
it('should accept jwt token on get', async () => {
const storageClient = new StorageClient(config);
const storageClientCalls = nock(config.uri)
// @ts-ignore: Nock .d.ts are outdated.
.matchHeader('authorization', `Bearer ${config.jwtSecret}`)
.get('/objects/1')
.reply(200, streamResponse(data));
const response = await storageClient.readStream('1');
expect(storageClientCalls.isDone()).to.be.true;
expect(response.data).to.be.instanceOf(Readable);
const resultData = JSON.parse(await getStream(response.data));
expect(resultData).to.be.deep.equal(data);
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc