Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: updated and decoupled contextService logic #72

Merged
merged 6 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/config/context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './model';
export * from './service';
25 changes: 25 additions & 0 deletions src/config/context/model.spec.ts
Original file line number Diff line number Diff line change
@@ -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();
});
});

57 changes: 57 additions & 0 deletions src/config/context/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { injectable } 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) => Context | undefined
}

@injectable()
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) {
console.warn(error);// eslint-disable-line no-undef, no-console
return undefined;
}
}

save(context: Context) {
try {
fs.writeFileSync(this.contextFilePath, JSON.stringify({
current: context.current,
store: context.store
}), { encoding: 'utf8' });
return context;
} catch (error) {
console.warn(error);// eslint-disable-line no-undef, no-console
return undefined;
}
}
}

71 changes: 71 additions & 0 deletions src/config/context/service.spec.ts
Original file line number Diff line number Diff line change
@@ -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 undefined;
}
}

class MockPositiveContextAllocator implements IContextAllocator {
load() {
return new Context({
current: 'test',
store: {
test: './test/specification.yml',
check: './test/specfication.yml'
}
});
}

save(context: Context) {
return 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();
});
});
52 changes: 52 additions & 0 deletions src/config/context/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Context, IContextAllocator, IContext, ContextAllocator } from './model';
import { injectable, inject, container } from 'tsyringe';

@injectable()
export class ContextService {
private _context: Context | undefined
constructor(
@inject(ContextAllocator) private contextAllocator: IContextAllocator
) {
this._context = this.contextAllocator.load();
}

get context() {
return this._context;
}

addContext(contextName: string, filePath: string): Context | undefined {
if (this._context) {
this._context.store[contextName as string] = filePath;
return this.contextAllocator.save(this._context);
}
this._context = new Context(this.createNewContext(contextName, filePath));
return this.contextAllocator.save(this._context);
}

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 undefined;
}

updateCurrent(contextName: string): Context | undefined {
if (this._context && this._context.getContext(contextName)) {
this._context.current = contextName;
return this.contextAllocator.save(this._context);
}
return undefined;
}

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;
}
}
Empty file added src/config/index.ts
Empty file.