From e5aa4aa860558e2997e125a633643c1485781212 Mon Sep 17 00:00:00 2001 From: Ara Park Date: Wed, 16 Sep 2020 20:32:43 +0900 Subject: [PATCH] Version 1.0.5 (#22) (#24) --- README.md | 138 +++++++++++++++++- package.json | 10 +- package/conev-sync-core/package.json | 2 +- package/conev-sync-core/src/cache/cache.ts | 25 ++++ .../conev-sync-core/src/config/config.spec.ts | 66 ++++++--- package/conev-sync-core/src/config/config.ts | 12 +- package/conev-sync-core/tsconfig.json | 3 +- package/conev-sync-json-source/tsconfig.json | 2 +- script/change-package.js | 2 +- src/index.ts | 8 +- tsconfig.json | 3 +- 11 files changed, 238 insertions(+), 33 deletions(-) create mode 100644 package/conev-sync-core/src/cache/cache.ts diff --git a/README.md b/README.md index 9921653..88e6fee 100644 --- a/README.md +++ b/README.md @@ -1 +1,137 @@ -# conev sync +# conev-sync +Conev-sync is a synchronous version of [conev](#reference). You can save, manage and use configuration through it. + +## Contents + + - [Install](#install) + - [Usage](#usage) + - [ConfigBuilder](#configbuilder) + - [Config](#config) + - [Source](#source) + - [Sources](#sources) + - [JsonSource](#jsonsource) + - [Example](#example) + - [Reference](#reference) +## Install +```shell +npm install conev-sync +``` + +## Usage + +Get ConfigBuilder from conev-sync and one or more Sources to use. In this example, the built-in JsonSource is used. + +```typescript +import { ConfigBuilder, JsonSource } from 'conev-sync'; +``` + + +Then, create Sources and set up. + +```typescript +const jsonSource = new JsonSource(); + +jsonSource + .set('basic', basic) //basic is JSON + .set('dev', dev) //dev is JSON + .set('prd', prd); //prd is JSON +``` + + +Create ConfigBuilder and add Environment, Source. + +```typescript +const builder = new ConfigBuilder(); + +builder + .addEnv('dev', 'basic') + .addSource(jsonSource); +``` + + +Build configuration. + +```typescript +const config = builder.build(); // This is the result of combining dev and basic. +``` + + +Use configuration. + +```typescript +config.get(); // The whole configuration created comes out +config.get('a.b.c'); // Is same as config.get().a.b.c + ``` + + + +*Each of Environments and Sources are merged by [deepmerge](#reference).(What is added first has high priority)* +You can set deepmerge options as follow : + +```typescript +builder.setOptions(options); +``` + +### ConfigBuilder + +```typescript +class ConfigBuilder { + addSource(...sources: Source[]): ConfigBuilder; + addEnv(...envs: string[]): ConfigBuilder; + setOptions(options?: Options): ConfigBuilder; + build(): Config; +} +``` +`ConfigBuilder` takes a configuration from the source and creates a new configuration according to the environment. `Env` and `Source` have priority. If priority is not defined, highest priority is added first. + +### Config + +```typescript +class Config { + constructor(sources: Source[], envs: string[], options?: Options); + sync(): Config; + get(key = ''): unknown | undefined; +} +``` +`config` is a container for configuration. `config` is provided by creating a new configuration from the configuration and environment obtained from `source`. + +### Source + +```typescript +interface Source { + export(): Map>; +} +``` +`Source` defines the source from which to get the configuration. Map is returned as the result value of `export`. The key of this map is environment and the value is the configuration when environment. + +You can make custom `Source` and use that. + +### Sources + +```typescript +class Sources implements Source { + constructor(sources: Source[], options?: Options); + add(source: Source, priority = -1): Sources; + export(): Map>; +} +``` +`Sources` defines the source by merging several sources. Use `add` to add source for new source. Map is returned as the result value of `export`. The key of this map is environment and the value is the configuration when environment. + + +### JsonSource + +```typescript +export default class JsonSource { + constructor(); + set(env: string, json: Record): JsonSource; + export(): Map>; +} +``` +`JsonSource` defines the source from JSON. Use `set` to add a configuration for a new environment. Map is returned as the result value of `export`. The key of this map is environment and the value is the configuration when environment. + +## Example + +## Reference + + - [conev](https://github.com/CourseDesign/conev) + - [deepmerge](https://github.com/TehShrike/deepmerge) diff --git a/package.json b/package.json index a28a174..cdb5f00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "conev-sync", - "version": "1.0.2", + "version": "1.0.5", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -24,14 +24,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/CourseDesign/design-system.git" + "url": "git+https://github.com/CourseDesign/conev-sync.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/CourseDesign/design-system/issues" + "url": "https://github.com/CourseDesign/conev-sync/issues" }, - "homepage": "https://github.com/CourseDesign/design-system#readme", + "homepage": "https://github.com/CourseDesign/conev-sync#readme", "husky": { "hooks": { "pre-push": "npm run test", @@ -83,7 +83,7 @@ "typescript": "^4.0.2" }, "dependencies": { - "conev-sync-core": "^1.0.4", + "conev-sync-core": "^1.0.5", "conev-sync-json-source": "^1.0.4" } } diff --git a/package/conev-sync-core/package.json b/package/conev-sync-core/package.json index bcffbe0..c5fa4d4 100644 --- a/package/conev-sync-core/package.json +++ b/package/conev-sync-core/package.json @@ -1,6 +1,6 @@ { "name": "conev-sync-core", - "version": "1.0.4", + "version": "1.0.5", "description": "", "main": "dist/index.js", "types" : "dist/index.d.ts", diff --git a/package/conev-sync-core/src/cache/cache.ts b/package/conev-sync-core/src/cache/cache.ts new file mode 100644 index 0000000..dd1187c --- /dev/null +++ b/package/conev-sync-core/src/cache/cache.ts @@ -0,0 +1,25 @@ +export default class Cache { + private readonly provider: (key: K) => V | undefined; + + private readonly values: Map; + + constructor(provider: (key: K) => V | undefined) { + this.provider = provider; + this.values = new Map(); + } + + refresh(): Cache { + this.values.clear(); + return this; + } + + get(key: K): V | undefined { + if (!this.values.has(key)) { + const value = this.provider(key); + if (value !== undefined) { + this.values.set(key, value); + } + } + return this.values.get(key); + } +} diff --git a/package/conev-sync-core/src/config/config.spec.ts b/package/conev-sync-core/src/config/config.spec.ts index 135246f..4b8fcc1 100644 --- a/package/conev-sync-core/src/config/config.spec.ts +++ b/package/conev-sync-core/src/config/config.spec.ts @@ -2,26 +2,24 @@ import Config from './config'; import SourceMock from '../source/source.mock'; describe('config', () => { - const source1 = new SourceMock( - new Map>([ - ['default', { source: '1', port: 3000, db: { type: 'postgres' } }], - ['develop', { env: 'develop', port: 3001 }], - ]) - ); - - const source2 = new SourceMock( - new Map>([ - [ - 'default', - { - source: '2', - port: 3000, - db: { port: 7070, username: 'test', type: 'mysql' }, - }, - ], - ['production', { env: 'production', port: 3002 }], - ]) - ); + const values1 = new Map>([ + ['default', { source: '1', port: 3000, db: { type: 'postgres' } }], + ['develop', { env: 'develop', port: 3001 }], + ]); + const source1 = new SourceMock(values1); + + const values2 = new Map>([ + [ + 'default', + { + source: '2', + port: 3000, + db: { port: 7070, username: 'test', type: 'mysql' }, + }, + ], + ['production', { env: 'production', port: 3002 }], + ]); + const source2 = new SourceMock(values2); test('get', () => { const config = new Config([source1, source2], ['default', 'develop']); @@ -33,6 +31,34 @@ describe('config', () => { db: { port: 7070, username: 'test', type: 'mysql' }, }); expect(config.get('source')).toEqual('2'); + expect(config.get('port')).toEqual(3001); + expect(config.get('db.port')).toEqual(7070); + }); + + test('sync', () => { + const config = new Config([source1, source2], ['default', 'develop']); + + expect(config.get()).toEqual({ + source: '2', + env: 'develop', + port: 3001, + db: { port: 7070, username: 'test', type: 'mysql' }, + }); + expect(config.get('source')).toEqual('2'); + expect(config.get('port')).toEqual(3001); + expect(config.get('db.port')).toEqual(7070); + + values2.set('develop', { port: 3002 }); + config.sync(); + + expect(config.get()).toEqual({ + source: '2', + env: 'develop', + port: 3002, + db: { port: 7070, username: 'test', type: 'mysql' }, + }); + expect(config.get('source')).toEqual('2'); + expect(config.get('port')).toEqual(3002); expect(config.get('db.port')).toEqual(7070); }); }); diff --git a/package/conev-sync-core/src/config/config.ts b/package/conev-sync-core/src/config/config.ts index d46d703..fadf486 100644 --- a/package/conev-sync-core/src/config/config.ts +++ b/package/conev-sync-core/src/config/config.ts @@ -1,6 +1,7 @@ import merge, { Options } from 'deepmerge'; import Sources from '../source/sources'; import Source from '../source/source'; +import Cache from '../cache/cache'; export default class Config { private readonly sources: Sources; @@ -11,16 +12,21 @@ export default class Config { private values: Record | null; + private cache: Cache; + constructor(sources: Source[], envs: string[], options?: Options) { this.sources = new Sources(sources, options); this.envs = envs; this.options = options; this.values = null; + this.cache = new Cache((key: string) => this.getValue(key)); this.sync(); } sync(): Config { + this.cache.refresh(); + const source = this.sources.export(); const configs = this.envs .map((env) => source.get(env)) @@ -31,7 +37,11 @@ export default class Config { return this; } - get(key = ''): unknown | null { + get(key = ''): unknown | undefined { + return this.cache.get(key); + } + + private getValue(key = ''): unknown | undefined { const tokens: string[] = key.split('.').reverse(); let current: any = this.values; diff --git a/package/conev-sync-core/tsconfig.json b/package/conev-sync-core/tsconfig.json index 0bc9841..2b3d15f 100644 --- a/package/conev-sync-core/tsconfig.json +++ b/package/conev-sync-core/tsconfig.json @@ -10,6 +10,7 @@ /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ "lib": [ "es2017", + "es2015", "dom" ], /* Specify library files to be included in the compilation. */ @@ -48,7 +49,7 @@ /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a proxy. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ diff --git a/package/conev-sync-json-source/tsconfig.json b/package/conev-sync-json-source/tsconfig.json index 0bc9841..d05c145 100644 --- a/package/conev-sync-json-source/tsconfig.json +++ b/package/conev-sync-json-source/tsconfig.json @@ -48,7 +48,7 @@ /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a proxy. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ diff --git a/script/change-package.js b/script/change-package.js index cf5ab22..d8c337c 100644 --- a/script/change-package.js +++ b/script/change-package.js @@ -16,7 +16,7 @@ if (key == null) { const value = process.argv[4]; if (value == null) { - console.error('Value is undefined!'); + console.error('Proxy is undefined!'); process.exit(1); } diff --git a/src/index.ts b/src/index.ts index 1e62e1a..12bab49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,8 @@ -export * from 'conev-sync-core'; +export { + default, + ConfigBuilder, + Config, + Source, + Sources, +} from 'conev-sync-core'; export { default as JsonSource } from 'conev-sync-json-source'; diff --git a/tsconfig.json b/tsconfig.json index 0bc9841..2b3d15f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ "lib": [ "es2017", + "es2015", "dom" ], /* Specify library files to be included in the compilation. */ @@ -48,7 +49,7 @@ /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a proxy. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */