From 4e544ede5ad15b135b7cb791df4961a0cce84495 Mon Sep 17 00:00:00 2001 From: Souvik Date: Mon, 20 Sep 2021 15:38:40 +0530 Subject: [PATCH 1/6] feat: updated and decoupled contextService logic --- src/config/context/index.ts | 2 + src/config/context/model.spec.ts | 25 +++++++++++ src/config/context/model.ts | 58 ++++++++++++++++++++++++ src/config/context/service.spec.ts | 71 ++++++++++++++++++++++++++++++ src/config/context/service.ts | 47 ++++++++++++++++++++ src/config/index.ts | 0 6 files changed, 203 insertions(+) create mode 100644 src/config/context/index.ts create mode 100644 src/config/context/model.spec.ts create mode 100644 src/config/context/model.ts create mode 100644 src/config/context/service.spec.ts create mode 100644 src/config/context/service.ts create mode 100644 src/config/index.ts diff --git a/src/config/context/index.ts b/src/config/context/index.ts new file mode 100644 index 00000000000..05e8ee3c21f --- /dev/null +++ b/src/config/context/index.ts @@ -0,0 +1,2 @@ +export * from './model'; +export * from './service'; diff --git a/src/config/context/model.spec.ts b/src/config/context/model.spec.ts new file mode 100644 index 00000000000..d9b514fc4db --- /dev/null +++ b/src/config/context/model.spec.ts @@ -0,0 +1,25 @@ +import { Context, ContextAllocator } from './model'; +import { CONTEXTFILE_PATH, ContextTestingHelper } from '../../constants'; + +const testingHelper = new ContextTestingHelper(); + +const contextAllocator = new ContextAllocator(); + +describe('ContextAllocator should ', () => { + test('have test contextFile path', () => { + expect(contextAllocator.contextFilePath).toMatch(CONTEXTFILE_PATH); + }); + + test('load context from test env path if present', () => { + testingHelper.createDummyContextFile(); + const ctx = contextAllocator.load(); + expect(ctx).toBeDefined(); + expect(ctx instanceof Context).toBeTruthy(); + }); + + test('return undefined if context file not present in root dir', () => { + testingHelper.deleteDummyContextFile(); + expect(contextAllocator.load()).toBeUndefined(); + }); +}); + diff --git a/src/config/context/model.ts b/src/config/context/model.ts new file mode 100644 index 00000000000..c78b376a31e --- /dev/null +++ b/src/config/context/model.ts @@ -0,0 +1,58 @@ +import { injectable, registry, } from 'tsyringe'; +import fs from 'fs'; +import { CONTEXTFILE_PATH } from '../../constants'; + +export interface IContext { + readonly current?: string, + readonly store: { + [name: string]: string + } +} + +export class Context implements IContext { + current?: string | undefined; + store: { [name: string]: string; }; + + constructor(ctx: IContext) { + this.current = ctx.current; + this.store = ctx.store; + } + + getContext(contextName: string) { + return this.store[contextName as string]; + } +} + +export interface IContextAllocator { + contextFilePath?: string + load: () => Context | undefined + save: (context: Context) => boolean +} + +@injectable() +@registry([ + { + token: 'IContextAllocator', + useToken: ContextAllocator + } +]) +export class ContextAllocator implements IContextAllocator { + contextFilePath = CONTEXTFILE_PATH; + load(): Context | undefined { + try { + return new Context(JSON.parse(fs.readFileSync(this.contextFilePath, 'utf8')) as IContext); + } catch (error) { + return undefined; + } + } + + save(context: Context) { + try { + fs.writeFileSync(this.contextFilePath, JSON.stringify(context), { encoding: 'utf8' }); + return true; + } catch (error) { + return false; + } + } +} + diff --git a/src/config/context/service.spec.ts b/src/config/context/service.spec.ts new file mode 100644 index 00000000000..0717e3b8d81 --- /dev/null +++ b/src/config/context/service.spec.ts @@ -0,0 +1,71 @@ +import { ContextService } from './service'; +import { Context, IContextAllocator } from './model'; + +class MockNegativeContextAllocator implements IContextAllocator { + contextFilePath?: string | undefined; + load() { + return undefined; + } + + save(_context: Context) { + return true; + } +} + +class MockPositiveContextAllocator implements IContextAllocator { + load() { + return new Context({ + current: 'test', + store: { + test: './test/specification.yml', + check: './test/specfication.yml' + } + }); + } + + save(context: Context) { + return context instanceof Context; + } +} + +describe('ContextService should', () => { + test('load undefined context', () => { + const contextService = new ContextService(new MockNegativeContextAllocator()); + expect(contextService.context).toBeUndefined(); + }); +}); + +let ctxService: ContextService; + +describe('ContextService should', () => { + beforeEach(() => { + ctxService = new ContextService(new MockPositiveContextAllocator()); + }); + + test('load context when available', () => { + const ctx = ctxService.context; + expect(ctx).toBeDefined(); + }); + + test('successfully save context', () => { + expect(ctxService.addContext('home', 'path')).toBeTruthy(); + }); + + test('successfully delete context', () => { + const res = ctxService.deleteContext('test'); + expect(res).toBeTruthy(); + expect(ctxService.context?.getContext('test')).toBeUndefined(); + }); + + test('return false when context not find to delete', () => { + expect(ctxService.deleteContext('home')).toBeFalsy(); + }); + + test('update the current context if present', () => { + expect(ctxService.updateCurrent('check')).toBeTruthy(); + }); + + test('return false if context is not present', () => { + expect(ctxService.updateCurrent('home')).toBeFalsy(); + }); +}); diff --git a/src/config/context/service.ts b/src/config/context/service.ts new file mode 100644 index 00000000000..c8d89950ac0 --- /dev/null +++ b/src/config/context/service.ts @@ -0,0 +1,47 @@ +import { Context, IContextAllocator } from './model'; +import { injectable, inject, container } from 'tsyringe'; + +@injectable() +export class ContextService { + private _context: Context | undefined + constructor( + @inject('IContextAllocator') private contextAllocator: IContextAllocator + ) { + this._context = this.contextAllocator.load(); + } + + get context() { + return this._context; + } + + addContext(contextName: string, filePath: string): boolean { + if (this._context) { + this._context.store[contextName as string] = filePath; + return this.contextAllocator.save(this._context); + } + return false; + } + + deleteContext(contextName: string) { + if (this._context && this._context.store[contextName as string]) { + delete this._context.store[contextName as string]; + return this.contextAllocator.save(this._context); + } + return false; + } + + updateCurrent(contextName: string) { + if (this._context && this._context.getContext(contextName)) { + this._context.current = contextName; + if (this.contextAllocator.save) { + this.contextAllocator.save(this._context); + } + return this.contextAllocator.save(this._context); + } + return false; + } + + static instantiate() { + return container.resolve(ContextService); + } +} diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 00000000000..e69de29bb2d From bca140c41e8d7b432126a4950780e97eaf34e17b Mon Sep 17 00:00:00 2001 From: Souvik Date: Mon, 20 Sep 2021 16:12:48 +0530 Subject: [PATCH 2/6] feat: creating new context when root context file is missing. --- src/config/context/service.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/config/context/service.ts b/src/config/context/service.ts index c8d89950ac0..684148e9e89 100644 --- a/src/config/context/service.ts +++ b/src/config/context/service.ts @@ -1,4 +1,4 @@ -import { Context, IContextAllocator } from './model'; +import { Context, IContextAllocator, IContext } from './model'; import { injectable, inject, container } from 'tsyringe'; @injectable() @@ -19,11 +19,13 @@ export class ContextService { this._context.store[contextName as string] = filePath; return this.contextAllocator.save(this._context); } - return false; + this._context = new Context(this.createNewContext(contextName, filePath)); + return this.contextAllocator.save(this._context); } deleteContext(contextName: string) { if (this._context && this._context.store[contextName as string]) { + if (this._context.current === contextName) { delete this._context.current; } delete this._context.store[contextName as string]; return this.contextAllocator.save(this._context); } @@ -33,9 +35,6 @@ export class ContextService { updateCurrent(contextName: string) { if (this._context && this._context.getContext(contextName)) { this._context.current = contextName; - if (this.contextAllocator.save) { - this.contextAllocator.save(this._context); - } return this.contextAllocator.save(this._context); } return false; @@ -44,4 +43,10 @@ export class ContextService { static instantiate() { return container.resolve(ContextService); } + + private createNewContext(contextName: string, filePath: string): IContext { + const ctx: IContext = { current: contextName, store: {} }; + ctx.store[contextName as string] = filePath; + return ctx; + } } From a5a8131fd7674dc610c65ccca1025b628fd76c6a Mon Sep 17 00:00:00 2001 From: Souvik Date: Tue, 21 Sep 2021 11:44:20 +0530 Subject: [PATCH 3/6] feat: removed registry & stringify issue --- src/config/context/model.ts | 13 +++++-------- src/config/context/service.ts | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/config/context/model.ts b/src/config/context/model.ts index c78b376a31e..b8fc9269625 100644 --- a/src/config/context/model.ts +++ b/src/config/context/model.ts @@ -1,4 +1,4 @@ -import { injectable, registry, } from 'tsyringe'; +import { injectable } from 'tsyringe'; import fs from 'fs'; import { CONTEXTFILE_PATH } from '../../constants'; @@ -30,12 +30,6 @@ export interface IContextAllocator { } @injectable() -@registry([ - { - token: 'IContextAllocator', - useToken: ContextAllocator - } -]) export class ContextAllocator implements IContextAllocator { contextFilePath = CONTEXTFILE_PATH; load(): Context | undefined { @@ -48,7 +42,10 @@ export class ContextAllocator implements IContextAllocator { save(context: Context) { try { - fs.writeFileSync(this.contextFilePath, JSON.stringify(context), { encoding: 'utf8' }); + fs.writeFileSync(this.contextFilePath, JSON.stringify({ + current: context.current, + store: context.store + }), { encoding: 'utf8' }); return true; } catch (error) { return false; diff --git a/src/config/context/service.ts b/src/config/context/service.ts index 684148e9e89..b20e50a4d01 100644 --- a/src/config/context/service.ts +++ b/src/config/context/service.ts @@ -1,11 +1,11 @@ -import { Context, IContextAllocator, IContext } from './model'; +import { Context, IContextAllocator, IContext, ContextAllocator } from './model'; import { injectable, inject, container } from 'tsyringe'; @injectable() export class ContextService { private _context: Context | undefined constructor( - @inject('IContextAllocator') private contextAllocator: IContextAllocator + @inject(ContextAllocator) private contextAllocator: IContextAllocator ) { this._context = this.contextAllocator.load(); } From 924e42649d3755e8c4b5700619bf75e37a82d292 Mon Sep 17 00:00:00 2001 From: Souvik Date: Tue, 21 Sep 2021 12:16:59 +0530 Subject: [PATCH 4/6] chore: returning context or undefined in contextAllocator save method --- src/config/context/model.ts | 6 +++--- src/config/context/service.spec.ts | 4 ++-- src/config/context/service.ts | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/config/context/model.ts b/src/config/context/model.ts index b8fc9269625..dcbc3319ef1 100644 --- a/src/config/context/model.ts +++ b/src/config/context/model.ts @@ -26,7 +26,7 @@ export class Context implements IContext { export interface IContextAllocator { contextFilePath?: string load: () => Context | undefined - save: (context: Context) => boolean + save: (context: Context) => Context | undefined } @injectable() @@ -46,9 +46,9 @@ export class ContextAllocator implements IContextAllocator { current: context.current, store: context.store }), { encoding: 'utf8' }); - return true; + return context; } catch (error) { - return false; + return undefined; } } } diff --git a/src/config/context/service.spec.ts b/src/config/context/service.spec.ts index 0717e3b8d81..c306f4d30ef 100644 --- a/src/config/context/service.spec.ts +++ b/src/config/context/service.spec.ts @@ -8,7 +8,7 @@ class MockNegativeContextAllocator implements IContextAllocator { } save(_context: Context) { - return true; + return undefined; } } @@ -24,7 +24,7 @@ class MockPositiveContextAllocator implements IContextAllocator { } save(context: Context) { - return context instanceof Context; + return context; } } diff --git a/src/config/context/service.ts b/src/config/context/service.ts index b20e50a4d01..e16931e0827 100644 --- a/src/config/context/service.ts +++ b/src/config/context/service.ts @@ -14,7 +14,7 @@ export class ContextService { return this._context; } - addContext(contextName: string, filePath: string): boolean { + addContext(contextName: string, filePath: string): Context | undefined { if (this._context) { this._context.store[contextName as string] = filePath; return this.contextAllocator.save(this._context); @@ -23,21 +23,21 @@ export class ContextService { return this.contextAllocator.save(this._context); } - deleteContext(contextName: string) { + deleteContext(contextName: string): Context | undefined { if (this._context && this._context.store[contextName as string]) { if (this._context.current === contextName) { delete this._context.current; } delete this._context.store[contextName as string]; return this.contextAllocator.save(this._context); } - return false; + return undefined; } - updateCurrent(contextName: string) { + updateCurrent(contextName: string): Context | undefined { if (this._context && this._context.getContext(contextName)) { this._context.current = contextName; return this.contextAllocator.save(this._context); } - return false; + return undefined; } static instantiate() { From 67c07aeaa6356aa691847080e7df301849bf338d Mon Sep 17 00:00:00 2001 From: Souvik Date: Tue, 21 Sep 2021 13:31:14 +0530 Subject: [PATCH 5/6] feat: added warning for saving file. --- src/config/context/model.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/context/model.ts b/src/config/context/model.ts index dcbc3319ef1..3b11711a047 100644 --- a/src/config/context/model.ts +++ b/src/config/context/model.ts @@ -1,3 +1,5 @@ +/* eslint-disable no-undef */ +/* eslint-disable no-console */ import { injectable } from 'tsyringe'; import fs from 'fs'; import { CONTEXTFILE_PATH } from '../../constants'; @@ -48,6 +50,7 @@ export class ContextAllocator implements IContextAllocator { }), { encoding: 'utf8' }); return context; } catch (error) { + console.warn(error); return undefined; } } From d808f23a4d27265f25c0ecaa47236b2c457825f7 Mon Sep 17 00:00:00 2001 From: Souvik Date: Tue, 21 Sep 2021 15:34:55 +0530 Subject: [PATCH 6/6] feat: lint fixes --- src/config/context/model.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/config/context/model.ts b/src/config/context/model.ts index 3b11711a047..a83c3edb89f 100644 --- a/src/config/context/model.ts +++ b/src/config/context/model.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-undef */ -/* eslint-disable no-console */ import { injectable } from 'tsyringe'; import fs from 'fs'; import { CONTEXTFILE_PATH } from '../../constants'; @@ -38,6 +36,7 @@ export class ContextAllocator implements IContextAllocator { try { return new Context(JSON.parse(fs.readFileSync(this.contextFilePath, 'utf8')) as IContext); } catch (error) { + console.warn(error);// eslint-disable-line no-undef, no-console return undefined; } } @@ -50,7 +49,7 @@ export class ContextAllocator implements IContextAllocator { }), { encoding: 'utf8' }); return context; } catch (error) { - console.warn(error); + console.warn(error);// eslint-disable-line no-undef, no-console return undefined; } }