11package com .flowforge .config
22
3- import cats .data .{NonEmptyList , ValidatedNel }
4- import cats .effect .{Sync , Resource }
3+ import cats .data .{ NonEmptyList , ValidatedNel }
4+ import cats .effect .{ Resource , Sync }
55import cats .syntax .all ._
6- import com .typesafe .config .{Config , ConfigFactory }
6+ import com .typesafe .config .{ Config , ConfigFactory }
77import scala .concurrent .duration ._
8- import scala .util .{Try , Success , Failure }
8+ import scala .util .{ Failure , Success , Try }
99
1010/**
11- * Type-safe configuration management system - replacement for CCM.
12- * Provides compile-time safe configuration loading with validation.
11+ * Type-safe configuration management system - replacement for CCM. Provides compile-time safe
12+ * configuration loading with validation.
1313 */
1414trait ConfigurationManagement [F [_]] {
15-
15+
1616 /**
17- * Load type-safe configuration with validation.
18- * Returns all validation errors if configuration is invalid.
17+ * Load type-safe configuration with validation. Returns all validation errors if configuration is
18+ * invalid.
1919 */
2020 def loadTypeSafeConfig [T : ConfigDecoder ](key : String ): F [ValidatedNel [ConfigError , T ]]
21-
21+
2222 /**
2323 * Load optional configuration - returns None if key doesn't exist.
2424 */
2525 def loadOptionalConfig [T : ConfigDecoder ](key : String ): F [Option [T ]]
26-
26+
2727 /**
2828 * Watch configuration for changes - returns stream of updates.
2929 */
3030 def watchConfig [T : ConfigDecoder ](key : String ): fs2.Stream [F , T ]
31-
31+
3232 /**
3333 * Refresh configuration from source.
3434 */
3535 def refreshConfig : F [Unit ]
36-
36+
3737 /**
3838 * Load configuration for specific environment.
3939 */
4040 def loadForEnvironment [T : ConfigDecoder ](
41- key : String ,
41+ key : String ,
4242 env : Environment
4343 ): F [ValidatedNel [ConfigError , T ]]
4444}
@@ -61,15 +61,15 @@ object ConfigError {
6161 case class MissingKey (key : String ) extends ConfigError {
6262 override def message : String = s " Configuration key ' $key' is missing "
6363 }
64-
64+
6565 case class InvalidType (key : String , expected : String , actual : String ) extends ConfigError {
6666 override def message : String = s " Configuration key ' $key' expected $expected but got $actual"
6767 }
68-
68+
6969 case class ValidationFailed (key : String , reason : String ) extends ConfigError {
7070 override def message : String = s " Configuration validation failed for ' $key': $reason"
7171 }
72-
72+
7373 case class ParseError (key : String , error : String ) extends ConfigError {
7474 override def message : String = s " Failed to parse configuration ' $key': $error"
7575 }
@@ -82,14 +82,14 @@ sealed trait Environment extends Product with Serializable
8282
8383object Environment {
8484 case object Development extends Environment
85- case object Staging extends Environment
86- case object Production extends Environment
87-
85+ case object Staging extends Environment
86+ case object Production extends Environment
87+
8888 def fromString (env : String ): Option [Environment ] = env.toLowerCase match {
8989 case " dev" | " development" => Some (Development )
90- case " staging" | " stage" => Some (Staging )
90+ case " staging" | " stage" => Some (Staging )
9191 case " prod" | " production" => Some (Production )
92- case _ => None
92+ case _ => None
9393 }
9494}
9595
@@ -172,66 +172,64 @@ case class AuditConfig(
172172)
173173
174174object ConfigurationManagement {
175-
175+
176176 /**
177177 * Create ConfigurationManagement instance.
178178 */
179- def apply [F [_]: ConfigurationManagement ]: ConfigurationManagement [F ] =
179+ def apply [F [_]: ConfigurationManagement ]: ConfigurationManagement [F ] =
180180 implicitly[ConfigurationManagement [F ]]
181-
181+
182182 /**
183183 * Default implementation using Typesafe Config.
184184 */
185- implicit def forTypesafeConfig [F [_]: Sync ]: ConfigurationManagement [F ] =
185+ implicit def forTypesafeConfig [F [_]: Sync ]: ConfigurationManagement [F ] =
186186 new TypesafeConfigManagement [F ]
187-
187+
188188 private class TypesafeConfigManagement [F [_]: Sync ] extends ConfigurationManagement [F ] {
189-
189+
190190 private val config = ConfigFactory .load()
191-
192- override def loadTypeSafeConfig [T : ConfigDecoder ](key : String ): F [ValidatedNel [ConfigError , T ]] = {
191+
192+ override def loadTypeSafeConfig [T : ConfigDecoder ](
193+ key : String
194+ ): F [ValidatedNel [ConfigError , T ]] =
193195 Sync [F ].delay {
194196 if (config.hasPath(key)) {
195197 ConfigDecoder [T ].decode(config, key)
196198 } else {
197199 ConfigError .MissingKey (key).invalidNel
198200 }
199201 }
200- }
201-
202- override def loadOptionalConfig [T : ConfigDecoder ](key : String ): F [Option [T ]] = {
202+
203+ override def loadOptionalConfig [T : ConfigDecoder ](key : String ): F [Option [T ]] =
203204 Sync [F ].delay {
204205 if (config.hasPath(key)) {
205206 ConfigDecoder [T ].decode(config, key).toOption
206207 } else {
207208 None
208209 }
209210 }
210- }
211-
212- override def watchConfig [T : ConfigDecoder ](key : String ): fs2.Stream [F , T ] = {
211+
212+ override def watchConfig [T : ConfigDecoder ](key : String ): fs2.Stream [F , T ] =
213213 // TODO: Implement configuration watching with file system monitoring
214214 fs2.Stream .empty
215- }
216-
217- override def refreshConfig : F [Unit ] = {
215+
216+ override def refreshConfig : F [Unit ] =
218217 Sync [F ].delay {
219218 // TODO: Implement configuration refresh
220219 ()
221220 }
222- }
223-
221+
224222 override def loadForEnvironment [T : ConfigDecoder ](
225- key : String ,
223+ key : String ,
226224 env : Environment
227225 ): F [ValidatedNel [ConfigError , T ]] = {
228226 val envKey = s " ${env.toString.toLowerCase}. $key"
229227 loadTypeSafeConfig[T ](envKey).flatMap { result =>
230228 result match {
231- case cats.data.Validated .Invalid (_) =>
229+ case cats.data.Validated .Invalid (_) =>
232230 // Fall back to non-environment specific key
233231 loadTypeSafeConfig[T ](key)
234- case valid =>
232+ case valid =>
235233 Sync [F ].pure(valid)
236234 }
237235 }
@@ -243,64 +241,65 @@ object ConfigurationManagement {
243241 * Configuration decoder instances for common types.
244242 */
245243object ConfigDecoder {
246-
244+
247245 def apply [T : ConfigDecoder ]: ConfigDecoder [T ] = implicitly[ConfigDecoder [T ]]
248-
246+
249247 // Basic type decoders
250248 implicit val stringDecoder : ConfigDecoder [String ] = new ConfigDecoder [String ] {
251- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , String ] = {
249+ override def decode (config : Config , path : String ): ValidatedNel [ConfigError , String ] =
252250 Try (config.getString(path)) match {
253251 case Success (value) => value.validNel
254- case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
252+ case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
255253 }
256- }
257254 }
258-
255+
259256 implicit val intDecoder : ConfigDecoder [Int ] = new ConfigDecoder [Int ] {
260- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , Int ] = {
257+ override def decode (config : Config , path : String ): ValidatedNel [ConfigError , Int ] =
261258 Try (config.getInt(path)) match {
262259 case Success (value) => value.validNel
263- case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
260+ case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
264261 }
265- }
266262 }
267-
263+
268264 implicit val booleanDecoder : ConfigDecoder [Boolean ] = new ConfigDecoder [Boolean ] {
269- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , Boolean ] = {
265+ override def decode (config : Config , path : String ): ValidatedNel [ConfigError , Boolean ] =
270266 Try (config.getBoolean(path)) match {
271267 case Success (value) => value.validNel
272- case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
268+ case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
273269 }
274- }
275270 }
276-
277- implicit val finiteDurationDecoder : ConfigDecoder [FiniteDuration ] = new ConfigDecoder [FiniteDuration ] {
278- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , FiniteDuration ] = {
279- Try (config.getDuration(path).toMillis.millis) match {
280- case Success (value) => value.validNel
281- case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
282- }
271+
272+ implicit val finiteDurationDecoder : ConfigDecoder [FiniteDuration ] =
273+ new ConfigDecoder [FiniteDuration ] {
274+ override def decode (config : Config , path : String ): ValidatedNel [ConfigError , FiniteDuration ] =
275+ Try (config.getDuration(path).toMillis.millis) match {
276+ case Success (value) => value.validNel
277+ case Failure (ex) => ConfigError .ParseError (path, ex.getMessage).invalidNel
278+ }
283279 }
284- }
285-
280+
286281 // Complex type decoders
287- implicit val pipelineConfigDecoder : ConfigDecoder [PipelineConfig ] = new ConfigDecoder [PipelineConfig ] {
288- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , PipelineConfig ] = {
289- val pipelineConfig = config.getConfig(path)
290-
291- (
292- stringDecoder.decode(pipelineConfig, " name" ),
293- intDecoder.decode(pipelineConfig, " batchSize" ),
294- intDecoder.decode(pipelineConfig, " parallelism" ),
295- finiteDurationDecoder.decode(pipelineConfig, " timeout" )
296- ).mapN(PipelineConfig .apply)
282+ implicit val pipelineConfigDecoder : ConfigDecoder [PipelineConfig ] =
283+ new ConfigDecoder [PipelineConfig ] {
284+ override def decode (
285+ config : Config ,
286+ path : String
287+ ): ValidatedNel [ConfigError , PipelineConfig ] = {
288+ val pipelineConfig = config.getConfig(path)
289+
290+ (
291+ stringDecoder.decode(pipelineConfig, " name" ),
292+ intDecoder.decode(pipelineConfig, " batchSize" ),
293+ intDecoder.decode(pipelineConfig, " parallelism" ),
294+ finiteDurationDecoder.decode(pipelineConfig, " timeout" )
295+ ).mapN(PipelineConfig .apply)
296+ }
297297 }
298- }
299-
298+
300299 implicit val sparkConfigDecoder : ConfigDecoder [SparkConfig ] = new ConfigDecoder [SparkConfig ] {
301300 override def decode (config : Config , path : String ): ValidatedNel [ConfigError , SparkConfig ] = {
302301 val sparkConfig = config.getConfig(path)
303-
302+
304303 (
305304 stringDecoder.decode(sparkConfig, " appName" ),
306305 stringDecoder.decode(sparkConfig, " master" ),
@@ -309,56 +308,67 @@ object ConfigDecoder {
309308 ).mapN(SparkConfig .apply)
310309 }
311310 }
312-
311+
313312 // TODO: Add decoders for other configuration types as needed
314313 implicit val engineConfigDecoder : ConfigDecoder [EngineConfig ] = new ConfigDecoder [EngineConfig ] {
315314 override def decode (config : Config , path : String ): ValidatedNel [ConfigError , EngineConfig ] = {
316315 val engineConfig = config.getConfig(path)
317316 sparkConfigDecoder.decode(engineConfig, " spark" ).map(spark => EngineConfig (spark))
318317 }
319318 }
320-
321- implicit val monitoringConfigDecoder : ConfigDecoder [MonitoringConfig ] = new ConfigDecoder [MonitoringConfig ] {
322- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , MonitoringConfig ] = {
323- val monitoringConfig = config.getConfig(path)
324-
325- (
326- booleanDecoder.decode(monitoringConfig, " enableMetrics" ),
327- booleanDecoder.decode(monitoringConfig, " enableTracing" ),
328- intDecoder.decode(monitoringConfig, " metricsPort" )
329- ).mapN(MonitoringConfig .apply)
319+
320+ implicit val monitoringConfigDecoder : ConfigDecoder [MonitoringConfig ] =
321+ new ConfigDecoder [MonitoringConfig ] {
322+ override def decode (
323+ config : Config ,
324+ path : String
325+ ): ValidatedNel [ConfigError , MonitoringConfig ] = {
326+ val monitoringConfig = config.getConfig(path)
327+
328+ (
329+ booleanDecoder.decode(monitoringConfig, " enableMetrics" ),
330+ booleanDecoder.decode(monitoringConfig, " enableTracing" ),
331+ intDecoder.decode(monitoringConfig, " metricsPort" )
332+ ).mapN(MonitoringConfig .apply)
333+ }
330334 }
331- }
332-
335+
333336 implicit val auditConfigDecoder : ConfigDecoder [AuditConfig ] = new ConfigDecoder [AuditConfig ] {
334337 override def decode (config : Config , path : String ): ValidatedNel [ConfigError , AuditConfig ] = {
335338 val auditConfig = config.getConfig(path)
336-
339+
337340 (
338341 booleanDecoder.decode(auditConfig, " enableAuditing" ),
339342 stringDecoder.decode(auditConfig, " auditLogPath" )
340343 ).mapN(AuditConfig .apply)
341344 }
342345 }
343-
346+
344347 // Placeholder for connector config decoder (to be implemented when connectors are built)
345- implicit val connectorConfigDecoder : ConfigDecoder [ConnectorConfig ] = new ConfigDecoder [ConnectorConfig ] {
346- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , ConnectorConfig ] = {
347- ConnectorConfig ().validNel // Empty for now
348+ implicit val connectorConfigDecoder : ConfigDecoder [ConnectorConfig ] =
349+ new ConfigDecoder [ConnectorConfig ] {
350+ override def decode (
351+ config : Config ,
352+ path : String
353+ ): ValidatedNel [ConfigError , ConnectorConfig ] =
354+ ConnectorConfig ().validNel // Empty for now
348355 }
349- }
350-
351- implicit val flowForgeConfigDecoder : ConfigDecoder [FlowForgeConfig ] = new ConfigDecoder [FlowForgeConfig ] {
352- override def decode (config : Config , path : String ): ValidatedNel [ConfigError , FlowForgeConfig ] = {
353- val flowforgeConfig = config.getConfig(path)
354-
355- (
356- pipelineConfigDecoder.decode(flowforgeConfig, " pipeline" ),
357- engineConfigDecoder.decode(flowforgeConfig, " engines" ),
358- connectorConfigDecoder.decode(flowforgeConfig, " connectors" ),
359- monitoringConfigDecoder.decode(flowforgeConfig, " monitoring" ),
360- auditConfigDecoder.decode(flowforgeConfig, " audit" )
361- ).mapN(FlowForgeConfig .apply)
356+
357+ implicit val flowForgeConfigDecoder : ConfigDecoder [FlowForgeConfig ] =
358+ new ConfigDecoder [FlowForgeConfig ] {
359+ override def decode (
360+ config : Config ,
361+ path : String
362+ ): ValidatedNel [ConfigError , FlowForgeConfig ] = {
363+ val flowforgeConfig = config.getConfig(path)
364+
365+ (
366+ pipelineConfigDecoder.decode(flowforgeConfig, " pipeline" ),
367+ engineConfigDecoder.decode(flowforgeConfig, " engines" ),
368+ connectorConfigDecoder.decode(flowforgeConfig, " connectors" ),
369+ monitoringConfigDecoder.decode(flowforgeConfig, " monitoring" ),
370+ auditConfigDecoder.decode(flowforgeConfig, " audit" )
371+ ).mapN(FlowForgeConfig .apply)
372+ }
362373 }
363- }
364- }
374+ }
0 commit comments