@valentine-efagene/qshelter-common 2.0.32 → 2.0.33

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.
@@ -69,8 +69,13 @@ export declare class ConfigService {
69
69
  /**
70
70
  * Get database credentials - combines secret and infrastructure config
71
71
  * Returns a complete DatabaseCredentials object ready to use
72
+ * For test stage (LocalStack), reads directly from SSM parameters
72
73
  */
73
74
  getDatabaseCredentials(stage?: string): Promise<DatabaseCredentials>;
75
+ /**
76
+ * Get a secure parameter from SSM (with decryption)
77
+ */
78
+ getSecureParameter(name: string): Promise<string>;
74
79
  /**
75
80
  * Get encryption secrets from Secrets Manager
76
81
  */
@@ -8,8 +8,20 @@ export class ConfigService {
8
8
  cacheTimestamps = new Map();
9
9
  CACHE_TTL = 5 * 60 * 1000; // 5 minutes
10
10
  constructor(region = 'us-east-1') {
11
- this.ssmClient = new SSMClient({ region });
12
- this.secretsClient = new SecretsManagerClient({ region });
11
+ const stage = process.env.NODE_ENV || process.env.STAGE || 'dev';
12
+ const isLocalStack = stage === 'test' || process.env.LOCALSTACK_ENDPOINT;
13
+ // Configure AWS clients for LocalStack or AWS
14
+ const clientConfig = { region };
15
+ if (isLocalStack) {
16
+ const endpoint = process.env.LOCALSTACK_ENDPOINT || 'http://localhost:4566';
17
+ clientConfig.endpoint = endpoint;
18
+ clientConfig.credentials = {
19
+ accessKeyId: 'test',
20
+ secretAccessKey: 'test',
21
+ };
22
+ }
23
+ this.ssmClient = new SSMClient(clientConfig);
24
+ this.secretsClient = new SecretsManagerClient(clientConfig);
13
25
  }
14
26
  static getInstance(region) {
15
27
  if (!ConfigService.instance) {
@@ -92,24 +104,67 @@ export class ConfigService {
92
104
  /**
93
105
  * Get database credentials - combines secret and infrastructure config
94
106
  * Returns a complete DatabaseCredentials object ready to use
107
+ * For test stage (LocalStack), reads directly from SSM parameters
95
108
  */
96
109
  async getDatabaseCredentials(stage = process.env.NODE_ENV || 'dev') {
97
110
  const cacheKey = `db-credentials-${stage}`;
98
111
  const cached = this.getFromCache(cacheKey);
99
112
  if (cached)
100
113
  return cached;
101
- const infraConfig = await this.getInfrastructureConfig(stage);
102
- const dbSecret = await this.getSecret(infraConfig.databaseSecretArn);
103
- const credentials = {
104
- username: dbSecret.username,
105
- password: dbSecret.password,
106
- host: infraConfig.dbHost,
107
- port: infraConfig.dbPort,
108
- database: `qshelter_${stage}`,
109
- };
114
+ let credentials;
115
+ // For test stage (LocalStack), read directly from SSM parameters
116
+ if (stage === 'test') {
117
+ const pathPrefix = `/qshelter/${stage}/`;
118
+ const host = await this.getParameter(`${pathPrefix}DB_HOST`);
119
+ const port = await this.getParameter(`${pathPrefix}DB_PORT`);
120
+ const username = await this.getParameter(`${pathPrefix}DB_USER`);
121
+ const password = await this.getSecureParameter(`${pathPrefix}DB_PASSWORD`);
122
+ const database = await this.getParameter(`${pathPrefix}DB_NAME`);
123
+ credentials = {
124
+ username,
125
+ password,
126
+ host,
127
+ port: parseInt(port, 10),
128
+ database,
129
+ };
130
+ }
131
+ else {
132
+ // For dev/prod, use infrastructure config + Secrets Manager
133
+ const infraConfig = await this.getInfrastructureConfig(stage);
134
+ const dbSecret = await this.getSecret(infraConfig.databaseSecretArn);
135
+ credentials = {
136
+ username: dbSecret.username,
137
+ password: dbSecret.password,
138
+ host: infraConfig.dbHost,
139
+ port: infraConfig.dbPort,
140
+ database: `qshelter_${stage}`,
141
+ };
142
+ }
110
143
  this.setCache(cacheKey, credentials);
111
144
  return credentials;
112
145
  }
146
+ /**
147
+ * Get a secure parameter from SSM (with decryption)
148
+ */
149
+ async getSecureParameter(name) {
150
+ const cached = this.getFromCache(`secure-${name}`);
151
+ if (cached)
152
+ return cached;
153
+ try {
154
+ const command = new GetParameterCommand({
155
+ Name: name,
156
+ WithDecryption: true,
157
+ });
158
+ const response = await this.ssmClient.send(command);
159
+ const value = response.Parameter?.Value || '';
160
+ this.setCache(`secure-${name}`, value);
161
+ return value;
162
+ }
163
+ catch (error) {
164
+ console.error(`Error fetching secure parameter ${name}:`, error);
165
+ throw new Error(`Failed to load secure parameter ${name}: ${error.message}`);
166
+ }
167
+ }
113
168
  /**
114
169
  * Get encryption secrets from Secrets Manager
115
170
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valentine-efagene/qshelter-common",
3
- "version": "2.0.32",
3
+ "version": "2.0.33",
4
4
  "description": "Shared database schemas and utilities for QShelter services",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -672,7 +672,7 @@ model PaymentPlan {
672
672
  // Fund collection behavior
673
673
  // true = we collect funds via wallet/gateway (e.g., downpayment)
674
674
  // false = external payment, we only track/reconcile (e.g., bank mortgage)
675
- collectFunds Boolean @default(true)
675
+ collectFunds Boolean @default(true)
676
676
 
677
677
  createdAt DateTime @default(now())
678
678
  updatedAt DateTime @updatedAt
@@ -773,7 +773,7 @@ model PropertyPaymentMethodPhase {
773
773
  // Fund collection behavior (inherited from PaymentPlan if not set)
774
774
  // true = we collect funds via wallet/gateway (e.g., downpayment)
775
775
  // false = external payment, we only track/reconcile (e.g., bank mortgage)
776
- collectFunds Boolean? // null = inherit from PaymentPlan
776
+ collectFunds Boolean? // null = inherit from PaymentPlan
777
777
 
778
778
  // Activation rules
779
779
  requiresPreviousPhaseCompletion Boolean @default(true)
@@ -941,7 +941,7 @@ model ContractPhase {
941
941
  // Fund collection behavior (snapshotted from template at contract creation)
942
942
  // true = we collect funds via wallet/gateway (e.g., downpayment)
943
943
  // false = external payment, we only track/reconcile (e.g., bank mortgage)
944
- collectFunds Boolean @default(true)
944
+ collectFunds Boolean @default(true)
945
945
 
946
946
  // Progress counters (for efficient activation checks)
947
947
  approvedDocumentsCount Int @default(0)