Skip to content

Commit

Permalink
feat(storage): add redis storage
Browse files Browse the repository at this point in the history
* re-organize types
* add composite storage
* tests for redis storage and composition storage
  • Loading branch information
eladav committed Jul 29, 2019
1 parent 3981470 commit 61a5229
Show file tree
Hide file tree
Showing 31 changed files with 611 additions and 76 deletions.
63 changes: 63 additions & 0 deletions common/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Stream } from 'stream';

export interface Mismatches {
[dependency: string]: {
host: string;
component: string;
};
}

export type Maybe<T> = T | undefined;

export type Issues<T> = Record<string, Partial<T> & { mismatches: Mismatches }>;

export interface IndexStorage {
getIndex(): Promise<Index>;
upsertIndex(index: Index): Promise<void>;
}

export interface ComponentsStorage {
getComponentTree(): Promise<ComponentTree>;
getComponent(name: string, version: string): Promise<Maybe<ComponentGetter>>;
saveComponent(component: Component, files: File[]): Promise<void>;
}

export type Storage = IndexStorage & ComponentsStorage;

export type Index = Record<
Host['id'],
{
dependencies: Dependencies;
components: Record<Component['name'], Required<Component>['version']>;
}
>;

export type Dependencies = Record<string, string>;

export interface Component {
name: string;
version?: string;
}

export type ComponentTree = Record<
Component['name'],
Record<ComponentTreeItem['version'], ComponentTreeItem['getDependencies']>
>;

export interface ComponentTreeItem extends Required<Component> {
getDependencies: () => Promise<Dependencies>;
}

export interface Host {
id: string;
dependencies: Dependencies;
}

export interface File {
name: string;
stream: Stream;
}

export interface ComponentGetter extends Required<Component> {
getCode: () => Promise<string>;
}
15 changes: 15 additions & 0 deletions common/types/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "@dynamico/common-types",
"version": "0.0.1-alpha.6",
"description": "Common types for Dynamico",
"main": "./dist/index.ts",
"author": "Soluto",
"private": true,
"scripts": {
"build": "npm run clean && npm run compile",
"compile": "tsc",
"clean": "rm -rf ./dist"
},
"license": "MIT"
}

9 changes: 9 additions & 0 deletions common/types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./",
"downlevelIteration": true
}
}

3 changes: 2 additions & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"packages": [
"server/*",
"client/*",
"examples/*"
"examples/*",
"common/*"
],
"version": "0.0.1-alpha.6",
"npmClient": "yarn",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"client/*",
"server/*",
"examples/*",
"dockerfiles/*"
"dockerfiles/*",
"common/*"
],
"scripts": {
"react-demo": "lerna run start --scope=react-dynamico-example --parallel",
Expand Down
5 changes: 5 additions & 0 deletions server/CompositionStorage/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
coverageDirectory: '../../coverage/CompositionStorage/'
};
138 changes: 138 additions & 0 deletions server/CompositionStorage/lib/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { CompositionStorage } from './';
import { IndexStorage, ComponentsStorage } from '@dynamico/common-types';
import { Index } from '@dynamico/common-types';

class MockIndexStorage implements IndexStorage {
getIndex: any = jest.fn();
upsertIndex: any = jest.fn();
}

class MockComponentsStorage implements ComponentsStorage {
getComponentTree: any = jest.fn();
getComponent: any = jest.fn();
saveComponent: any = jest.fn();
}
describe('CompositionStorage', () => {
describe('getIndex', () => {
it('Should proxy call to index storage', async () => {
const mockIndexStorage = new MockIndexStorage();
const mockComponentsStorage = new MockComponentsStorage();
const storage = new CompositionStorage(mockIndexStorage, mockComponentsStorage);
const expected = 'expected';
mockIndexStorage.getIndex.mockReturnValueOnce(Promise.resolve(expected));

const result = await storage.getIndex();

expect(result).toEqual(expected);
expect(mockIndexStorage.getIndex).toBeCalled();
expect(mockIndexStorage.upsertIndex).not.toBeCalled();
expect(mockComponentsStorage.getComponent).not.toBeCalled();
expect(mockComponentsStorage.getComponentTree).not.toBeCalled();
expect(mockComponentsStorage.saveComponent).not.toBeCalled();
});
});

describe('upsertIndex', () => {
it('Should proxy call to index storage', async () => {
const mockIndexStorage = new MockIndexStorage();
const mockComponentsStorage = new MockComponentsStorage();
const storage = new CompositionStorage(mockIndexStorage, mockComponentsStorage);
const expected: Index = {
something: {
components: {},
dependencies: {}
}
};
const mockInputIndex: Index = {
somthingElse: {
components: {},
dependencies: {}
}
};
mockIndexStorage.upsertIndex.mockReturnValueOnce(Promise.resolve(expected));

const result = await storage.upsertIndex(mockInputIndex);

expect(result).toEqual(expected);
expect(mockIndexStorage.upsertIndex).toBeCalledWith(mockInputIndex);
expect(mockIndexStorage.getIndex).not.toBeCalled();
expect(mockComponentsStorage.getComponent).not.toBeCalled();
expect(mockComponentsStorage.getComponentTree).not.toBeCalled();
expect(mockComponentsStorage.saveComponent).not.toBeCalled();
});
});

describe('getComponent', () => {
it('Should proxy call to components storage', async () => {
const mockIndexStorage = new MockIndexStorage();
const mockComponentsStorage = new MockComponentsStorage();
const storage = new CompositionStorage(mockIndexStorage, mockComponentsStorage);
const mockComponentName = 'some component';
const mockComponentVersion = 'some version';
const expectedReturnValue = {
name: mockComponentName,
version: mockComponentVersion,
getCode: () => 'something'
};
mockComponentsStorage.getComponent.mockReturnValueOnce(expectedReturnValue);

const result = await storage.getComponent(mockComponentName, mockComponentVersion);

expect(result).toEqual(expectedReturnValue);
expect(mockComponentsStorage.getComponent).toBeCalledWith(mockComponentName, mockComponentVersion);
expect(mockIndexStorage.upsertIndex).not.toBeCalled();
expect(mockIndexStorage.getIndex).not.toBeCalled();
expect(mockComponentsStorage.getComponentTree).not.toBeCalled();
expect(mockComponentsStorage.saveComponent).not.toBeCalled();
});
});

describe('getComponentTree', () => {
it('Should proxy call to components storage', async () => {
const mockIndexStorage = new MockIndexStorage();
const mockComponentsStorage = new MockComponentsStorage();
const storage = new CompositionStorage(mockIndexStorage, mockComponentsStorage);
const mockComponentName = 'some component';
const mockComponentVersion = 'some version';
const expectedReturnValue = {
[mockComponentName]: {
[mockComponentVersion]: () => 'something'
}
};
mockComponentsStorage.getComponentTree.mockReturnValueOnce(expectedReturnValue);

const result = await storage.getComponentTree();

expect(result).toEqual(expectedReturnValue);
expect(mockComponentsStorage.getComponentTree).toBeCalled();
expect(mockIndexStorage.upsertIndex).not.toBeCalled();
expect(mockIndexStorage.getIndex).not.toBeCalled();
expect(mockComponentsStorage.getComponent).not.toBeCalled();
expect(mockComponentsStorage.saveComponent).not.toBeCalled();
});
});

describe('saveComponent', () => {
it('Should proxy call to components storage', async () => {
const mockIndexStorage = new MockIndexStorage();
const mockComponentsStorage = new MockComponentsStorage();
const storage = new CompositionStorage(mockIndexStorage, mockComponentsStorage);
const mockComponentName = 'some component';
const mockComponentVersion = 'some version';
const mockComponent = {
name: mockComponentName,
version: mockComponentVersion
};
const files = ['some', 'files'];
mockComponentsStorage.saveComponent.mockReturnValueOnce(Promise.resolve());

await storage.saveComponent(mockComponent, files as any);

expect(mockComponentsStorage.saveComponent).toBeCalledWith(mockComponent, files);
expect(mockComponentsStorage.getComponentTree).not.toBeCalled();
expect(mockIndexStorage.upsertIndex).not.toBeCalled();
expect(mockIndexStorage.getIndex).not.toBeCalled();
expect(mockComponentsStorage.getComponent).not.toBeCalled();
});
});
});
31 changes: 31 additions & 0 deletions server/CompositionStorage/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
Storage,
IndexStorage,
ComponentsStorage,
Component,
File,
Index,
ComponentTree,
ComponentGetter,
Maybe
} from '@dynamico/common-types';

export class CompositionStorage implements Storage {
constructor(private indexStorage: IndexStorage, private componentsStorage: ComponentsStorage) {}

getIndex(): Promise<Index> {
return this.indexStorage.getIndex();
}
upsertIndex(index: Index): Promise<void> {
return this.indexStorage.upsertIndex(index);
}
getComponentTree(): Promise<ComponentTree> {
return this.componentsStorage.getComponentTree();
}
getComponent(name: string, version: string): Promise<Maybe<ComponentGetter>> {
return this.componentsStorage.getComponent(name, version);
}
saveComponent(component: Component, files: File[]): Promise<void> {
return this.componentsStorage.saveComponent(component, files);
}
}
34 changes: 34 additions & 0 deletions server/CompositionStorage/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@dynamico/composition-storage",
"version": "0.0.1-alpha.6",
"description": "Dynamico composition storage provider. Allows you to use different storages for index and components",
"author": "Soluto",
"homepage": "https://github.com/soluto/dynamico#readme",
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "npm run clean && npm run compile",
"test": "jest",
"coverage": "jest --coverage",
"compile": "tsc",
"clean": "rm -rf ./dist"
},
"publishConfig": {
"access": "public"
},
"directories": {
"lib": "dist"
},
"repository": {
"type": "git",
"url": "git+https://github.com/soluto/dynamico.git"
},
"bugs": {
"url": "https://github.com/soluto/dynamico/issues"
},
"bundledDependencies": ["@dynamico/common-types"],
"devDependencies": {
"@dynamico/common-types": "^0.0.1-alpha.6"
}
}
8 changes: 8 additions & 0 deletions server/CompositionStorage/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./lib"
}
}

2 changes: 1 addition & 1 deletion server/azure-blob-storage/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ContainerURL, Aborter, BlockBlobURL, uploadStreamToBlockBlob } from '@azure/storage-blob';
import { Storage, Component, File, Index, ComponentTree, ComponentGetter, Maybe } from '@dynamico/driver';
import { Storage, Component, File, Index, ComponentTree, ComponentGetter, Maybe } from '@dynamico/common-types';
import intoStream from 'into-stream';
import { Readable } from 'stream';
import { FailedIndexUpsert } from './errors';
Expand Down
3 changes: 2 additions & 1 deletion server/azure-blob-storage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@
},
"homepage": "https://github.com/soluto/dynamico#readme",
"dependencies": {
"@dynamico/driver": "^0.0.1-alpha.6",
"into-stream": "^5.1.0"
},
"peerDependencies": {
"@azure/storage-blob": "^10.3.0"
},
"bundledDependencies": ["@dynamico/common-types"],
"devDependencies": {
"@azure/storage-blob": "^10.3.0",
"@dynamico/common-types": "^0.0.1-alpha.6",
"@types/node": "^12.0.10",
"jest": "^24.8.0",
"stream-to-string": "^1.2.0",
Expand Down
3 changes: 2 additions & 1 deletion server/driver/lib/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { merge } from 'lodash';
import { Stream } from 'stream';

import { Driver, Storage, Component, ComponentTree, File, ComponentGetter, Index, Maybe } from './';
import { Storage, Component, ComponentTree, File, ComponentGetter, Index, Maybe } from '@dynamico/common-types';
import { Driver } from './';
import {
NoComponentError,
NoComponentVersionError,
Expand Down

0 comments on commit 61a5229

Please sign in to comment.