@alacard-project/config-sdk 1.1.1 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts DELETED
@@ -1,8 +0,0 @@
1
- export * from './clients/config.client';
2
- export * from './clients/vault.client';
3
- export * from './modules/config.module';
4
- export * from './utils/nest-helpers';
5
- export * from './constants';
6
- export * from './enums/env.enum';
7
- export * from './types/config.types';
8
- export * from './types/grpc.types';
@@ -1,28 +0,0 @@
1
- import { DynamicModule, Module, Global, Provider } from '@nestjs/common';
2
- import { ConfigClient } from '../clients/config.client';
3
- import { ConfigOptions } from '../types/config.types';
4
- import { CONFIG_OPTIONS } from '../constants';
5
-
6
- @Global()
7
- @Module({})
8
- export class ConfigModule {
9
- public static forRoot(options: ConfigOptions): DynamicModule {
10
- const optionsProvider: Provider = {
11
- provide: CONFIG_OPTIONS,
12
- useValue: options,
13
- };
14
-
15
- const configClientProvider: Provider = {
16
- provide: ConfigClient,
17
- useFactory: async (): Promise<ConfigClient> => {
18
- return await ConfigClient.initialize(options);
19
- },
20
- };
21
-
22
- return {
23
- module: ConfigModule,
24
- providers: [optionsProvider, configClientProvider],
25
- exports: [configClientProvider, optionsProvider],
26
- };
27
- }
28
- }
@@ -1,38 +0,0 @@
1
- export interface VaultOptions {
2
- address: string;
3
- roleId: string;
4
- secretId: string;
5
- pkiPath?: string;
6
- }
7
-
8
- export interface VaultCerts {
9
- ca: string;
10
- certificate: string;
11
- privateKey: string;
12
- }
13
-
14
- export interface TLSConfig {
15
- rootCert: Buffer;
16
- clientCert: Buffer;
17
- clientKey: Buffer;
18
- }
19
-
20
- export interface ConfigOptions {
21
- serviceName: string;
22
- environment: string;
23
- grpcUrl: string;
24
- version?: string;
25
- kafkaBrokers?: string[];
26
- useDotenvFallback?: boolean;
27
- internalKey?: string;
28
- tls?: TLSConfig;
29
- vault?: VaultOptions;
30
- }
31
-
32
- export interface ConfigUpdatedEvent {
33
- service: string;
34
- environment: string;
35
- version: string;
36
- key: string;
37
- value: string;
38
- }
@@ -1,15 +0,0 @@
1
- import {
2
- GetConfigRequest as GeneratedGetConfigRequest,
3
- ConfigResponse as GeneratedConfigResponse,
4
- SetConfigRequest as GeneratedSetConfigRequest,
5
- ListConfigsRequest as GeneratedListConfigsRequest,
6
- ListConfigsResponse as GeneratedListConfigsResponse,
7
- ServiceConfig as GeneratedServiceConfig
8
- } from '../generated/config';
9
-
10
- export interface GetConfigRequest extends GeneratedGetConfigRequest { }
11
- export interface ConfigResponse extends GeneratedConfigResponse { }
12
- export interface SetConfigRequest extends GeneratedSetConfigRequest { }
13
- export interface ListConfigsRequest extends GeneratedListConfigsRequest { }
14
- export interface ListConfigsResponse extends GeneratedListConfigsResponse { }
15
- export interface ServiceConfig extends GeneratedServiceConfig { }
@@ -1,12 +0,0 @@
1
- export interface ConfigMap {
2
- [key: string]: string;
3
- }
4
-
5
- export interface ConfigOptions {
6
- serviceName: string;
7
- environment: string;
8
- grpcUrl: string;
9
- version?: string;
10
- kafkaBrokers?: string[];
11
- useDotenvFallback?: boolean;
12
- }
@@ -1,69 +0,0 @@
1
- import { Environment } from '../enums/env.enum';
2
- import { ConfigClient } from '../clients/config.client';
3
-
4
- export abstract class BaseConfigService {
5
- constructor() { }
6
-
7
- public get<T = string>(key: string, defaultValue?: T): T {
8
- // 1. Try Config Client (if initialized)
9
- try {
10
- const client = ConfigClient.getInstance();
11
- const val = client.get(key);
12
- if (val !== undefined && val !== '') {
13
- return val as unknown as T;
14
- }
15
- } catch (_e) {
16
- // Client not initialized yet, fall through to process.env
17
- }
18
-
19
- // 2. Try process.env
20
- const envVal = process.env[key];
21
- if (envVal !== undefined) {
22
- return envVal as unknown as T;
23
- }
24
-
25
- return defaultValue as T;
26
- }
27
-
28
- public getRequiredString(key: string): string {
29
- const value = this.get<string>(key);
30
- if (!value && process.env.NODE_ENV !== Environment.TEST) {
31
- throw new Error(`Configuration Error: ${key} is required`);
32
- }
33
- return value || '';
34
- }
35
-
36
- public getRequiredNumber(key: string): number {
37
- const value = this.get<string>(key);
38
- if (!value && process.env.NODE_ENV !== Environment.TEST) {
39
- throw new Error(`Configuration Error: ${key} is required`);
40
- }
41
- return parseInt(value || '0', 10);
42
- }
43
-
44
- public getOptionalString(key: string, defaultValue: string): string {
45
- return this.get<string>(key, defaultValue) || defaultValue;
46
- }
47
-
48
- public getOptionalNumber(key: string, defaultValue: number): number {
49
- const value = this.get<string>(key);
50
- return value ? parseInt(value, 10) : defaultValue;
51
- }
52
-
53
- get environment(): Environment {
54
- return this.get<Environment>('NODE_ENV', Environment.DEVELOPMENT) || Environment.DEVELOPMENT;
55
- }
56
-
57
- get databaseUrl(): string {
58
- return this.getRequiredString('DATABASE_URL');
59
- }
60
-
61
- get kafkaBrokers(): string[] {
62
- const brokers = this.get<string>('KAFKA_BROKERS', 'localhost:9092');
63
- return (brokers || 'localhost:9092').split(',');
64
- }
65
-
66
- get isProduction(): boolean {
67
- return this.environment === Environment.PRODUCTION;
68
- }
69
- }
@@ -1,108 +0,0 @@
1
- import { ConfigClient } from '../src/clients/config.client';
2
- import * as grpc from '@grpc/grpc-js';
3
- import { Kafka } from 'kafkajs';
4
- import CircuitBreaker from 'opossum';
5
-
6
- // Mock gRPC generated code
7
- jest.mock('../src/generated/config', () => ({
8
- ConfigServiceClient: jest.fn().mockImplementation(() => ({
9
- getConfig: jest.fn().mockImplementation((req, meta, cb) => {
10
- cb(null, { values: { REMOTE_KEY: 'REMOTE_VALUE' } });
11
- }),
12
- })),
13
- }));
14
-
15
- jest.mock('@grpc/grpc-js');
16
- jest.mock('kafkajs');
17
- jest.mock('opossum');
18
- jest.mock('dotenv', () => ({
19
- config: jest.fn().mockReturnValue({ parsed: { DOTENV_KEY: 'DOTENV_VALUE' } })
20
- }));
21
-
22
- describe('ConfigClient', () => {
23
- const options = {
24
- serviceName: 'test-service',
25
- environment: 'test',
26
- grpcUrl: 'localhost:50051',
27
- kafkaBrokers: ['localhost:9092'],
28
- };
29
-
30
- let client: ConfigClient;
31
- let mockConsumer: any;
32
-
33
- beforeEach(async () => {
34
- jest.clearAllMocks();
35
-
36
- // Mock Kafka
37
- mockConsumer = {
38
- connect: jest.fn().mockResolvedValue(undefined),
39
- subscribe: jest.fn().mockResolvedValue(undefined),
40
- run: jest.fn().mockResolvedValue(undefined),
41
- };
42
- (Kafka as any).mockImplementation(() => ({
43
- consumer: () => mockConsumer,
44
- }));
45
-
46
- // Mock CircuitBreaker
47
- (CircuitBreaker as any).mockImplementation((fn: any) => ({
48
- fire: jest.fn().mockImplementation((...args: any[]) => fn(...args)),
49
- on: jest.fn(),
50
- }));
51
-
52
- // Mock grpc
53
- (grpc.credentials.createInsecure as any).mockReturnValue({});
54
-
55
- // Reset singleton
56
- (ConfigClient as any).instance = null;
57
- });
58
-
59
- it('should initialize and load config from various sources', async () => {
60
- process.env.APP_TEST_KEY = 'ENV_VALUE';
61
-
62
- client = await ConfigClient.initialize(options);
63
-
64
- expect(client.get('APP_TEST_KEY')).toBe('ENV_VALUE');
65
- expect(client.get('REMOTE_KEY')).toBe('REMOTE_VALUE');
66
- expect(client.get('DOTENV_KEY')).toBe('DOTENV_VALUE');
67
- });
68
-
69
- it('should return default value if key not found', async () => {
70
- client = await ConfigClient.initialize(options);
71
- expect(client.get('NON_EXISTENT', 'DEFAULT')).toBe('DEFAULT');
72
- });
73
-
74
- it('should handle numeric and boolean values', async () => {
75
- client = await ConfigClient.initialize(options);
76
- (client as any).configMap['INT_KEY'] = '42';
77
- (client as any).configMap['BOOL_KEY'] = 'true';
78
-
79
- expect(client.getInt('INT_KEY')).toBe(42);
80
- expect(client.getBool('BOOL_KEY')).toBe(true);
81
- });
82
-
83
- it('should handle hot-reload from Kafka', async () => {
84
- let kafkaHandler: any;
85
- mockConsumer.run.mockImplementation(({ eachMessage }: any) => {
86
- kafkaHandler = eachMessage;
87
- return Promise.resolve();
88
- });
89
-
90
- client = await ConfigClient.initialize(options);
91
-
92
- expect(kafkaHandler).toBeDefined();
93
-
94
- // Simulate Kafka message
95
- await kafkaHandler({
96
- message: {
97
- value: Buffer.from(JSON.stringify({
98
- service: 'test-service',
99
- environment: 'test',
100
- key: 'HOT_KEY',
101
- value: 'HOT_VALUE'
102
- }))
103
- }
104
- });
105
-
106
- expect(client.get('HOT_KEY')).toBe('HOT_VALUE');
107
- });
108
- });
@@ -1,62 +0,0 @@
1
- import { VaultClient } from '../src/clients/vault.client';
2
- import axios from 'axios';
3
-
4
- jest.mock('axios');
5
- const mockedAxios = axios as jest.Mocked<typeof axios>;
6
-
7
- describe('VaultClient', () => {
8
- const options = {
9
- address: 'http://localhost:8200',
10
- roleId: 'role-id',
11
- secretId: 'secret-id',
12
- };
13
-
14
- let client: VaultClient;
15
-
16
- beforeEach(() => {
17
- jest.clearAllMocks();
18
- mockedAxios.create.mockReturnValue(mockedAxios as any);
19
- client = new VaultClient(options);
20
- });
21
-
22
- it('should login and get secrets', async () => {
23
- mockedAxios.post.mockResolvedValueOnce({
24
- data: {
25
- auth: {
26
- client_token: 'test-token',
27
- lease_duration: 3600,
28
- },
29
- },
30
- });
31
-
32
- mockedAxios.get.mockResolvedValueOnce({
33
- data: {
34
- data: {
35
- data: {
36
- KEY: 'VALUE',
37
- },
38
- },
39
- },
40
- });
41
-
42
- const secrets = await client.getKVSecrets('path');
43
- expect(secrets).toEqual({ KEY: 'VALUE' });
44
- expect(mockedAxios.post).toHaveBeenCalledWith('/auth/approle/login', expect.any(Object));
45
- expect(mockedAxios.get).toHaveBeenCalledWith('/secret/data/path');
46
- });
47
-
48
- it('should handle 404 for secrets', async () => {
49
- mockedAxios.post.mockResolvedValueOnce({
50
- data: {
51
- auth: { client_token: 't', lease_duration: 3600 },
52
- },
53
- });
54
-
55
- mockedAxios.get.mockRejectedValueOnce({
56
- response: { status: 404 },
57
- });
58
-
59
- const secrets = await client.getKVSecrets('missing');
60
- expect(secrets).toEqual({});
61
- });
62
- });
package/tsconfig.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "CommonJS",
4
- "target": "ES2022",
5
- "declaration": true,
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true,
12
- "moduleResolution": "node"
13
- },
14
- "include": [
15
- "src/**/*"
16
- ],
17
- "exclude": [
18
- "node_modules",
19
- "dist"
20
- ]
21
- }