@elastic.io/maester-client
Version:
The official object-storage client
335 lines (328 loc) • 13.9 kB
text/typescript
/* eslint-disable max-len */
import 'mocha';
import chai, { expect } from 'chai';
import sinon from 'sinon';
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';
chai.use(chaiAsPromised);
process.env.ELASTICIO_OBJECT_STORAGE_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6I';
process.env.ELASTICIO_OBJECT_STORAGE_URI = 'https://ma.estr';
describe('ObjectStorageWrapper', () => {
const objectStorageWrapper = new ObjectStorageWrapper(getContext(), 'userAgent');
const genHeaders = (amount: number) => {
const resultHeaders = [];
for (let i = 0; i < amount; i++) {
resultHeaders.push({ key: `key${i}`, value: `value${i}` });
}
return resultHeaders;
};
const data = {
foo: 'bar',
};
const updatedData = {
foo: 'bar',
bap: 'baz',
};
const queryKey = 'baz';
const queryValue = 'bap';
const ttl = 10;
const id = 'id123';
const createObjectWithQueriableField = {
contentType: 'application/json',
createdAt: 1622811501107,
objectId: '2bd48165-119f-489d-8842-8d07b2c7cc1b',
metadata: {},
queriableFields: {
demosearchfield: 'qwerty',
},
};
let finalReqCfg;
afterEach(sinon.restore);
before(() => {
process.env.ELASTICIO_FLOW_ID = 'flow_id';
process.env.ELASTICIO_STEP_ID = 'step_id';
});
describe('Create object', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: createObjectWithQueriableField }));
});
describe('valid inputs', () => {
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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-query-key0': 'value0',
'x-eio-ttl': '10',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-query-key0': 'value0',
'x-query-key1': 'value1',
'x-meta-key0': 'value0',
'x-meta-key1': 'value1',
'x-meta-key2': 'value2',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
});
describe('invalid inputs', () => {
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');
});
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');
});
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}`);
});
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');
});
});
});
describe('Lookup object by ID', () => {
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(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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
});
describe('Lookup objects by query parameters', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({
data: streamFromObject([createObjectWithQueriableField, createObjectWithQueriableField])
}));
});
it('Should return 2 objects', async () => {
const result = await objectStorageWrapper.lookupObjectsByQueryParameters([{ key: queryKey, value: queryValue }]);
expect(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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
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 () => {
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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
it('Should successfully update an object with headers', async () => {
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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-query-key0': 'value0',
'x-query-key1': 'value1',
'x-query-key2': 'value2',
'x-meta-key0': 'value0',
'x-meta-key1': 'value1',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
});
describe('Invalid update request', () => {
it('Should throw an error', async () => {
await expect(
// @ts-ignore
objectStorageWrapper.updateObjectById(
id,
updatedData,
[
{ key: 'key0', value: 'value0' },
{ key: 'key0', value: 'value0' },
],
)
).to.be.rejectedWith('header key "key0" was already added');
});
});
});
describe('Delete object by ID', () => {
beforeEach(async () => {
finalReqCfg = sinon.stub(StorageClient.prototype, <any>'requestRetry').callsFake(async () => ({ data: '' }));
});
it('should delete object by ID', async () => {
const objectStorageWrapper2 = new ObjectStorageWrapper(getContext(), 'userAgent', 'msgId');
const result = await objectStorageWrapper2.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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-request-id': 'f:flow_id;s:step_id;m:msgId',
}
});
});
});
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 () => {
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',
'User-Agent': 'userAgent axios/^1.8.2',
'x-request-id': 'f:flow_id;s:step_id;m:',
}
});
});
});
});
});
});