-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(deriv_env): make package independent of env file (#318)
* get the env file content from outside * make Env class not being singleton * create EnvLoader class * fix the tests * bring back the removed test case * update the initialize doc * export classes need to be used by outside * export BaseEnv class as well * format deriv_env.dart * swape names for Env and EnvLoader to reduce the amount of change in the apps using this packagew * handle exception in bool type * remove duplicate lint rules * define common values as variable in env_test.dart * update README.md file * remove breaking change by making the laod method deprecated * Update CHANGELOG.md * udpate README.md file * refactor: Improve flexibility by removing dependency to env file - **Deprecation:** The `load` method is deprecated; it is recommended to use the new `initialize` method for improved flexibility. The `load` method will still function but may be removed in future releases. - Introduced a new method `initialize` to handle initialization without requiring an env file directly. - Improved package flexibility by removing the strict dependency on an env file for test environments.
- Loading branch information
1 parent
53ffa34
commit a7242c8
Showing
7 changed files
with
215 additions
and
138 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 9 additions & 6 deletions
15
packages/deriv_env/lib/base_env.dart → packages/deriv_env/lib/base_env_loader.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,19 @@ | ||
/// Base class for retrieve environment variables providers. | ||
abstract class BaseEnv { | ||
abstract class BaseEnvLoader { | ||
/// Returns `true` if [Env] is initialized, otherwise `false`. | ||
bool get isInitialized; | ||
|
||
/// Returns all environment variables as a [Map]. | ||
Map<String, dynamic> get entries; | ||
|
||
/// Loads environment variables from a `.env` file. | ||
/// | ||
/// If [filename] is not provided, it will default to `.env`. | ||
Future<void> load([String filename = '.env']); | ||
/// Loads environment variables. | ||
Future<void> loadEnvironment(); | ||
|
||
/// Retrieves an environment variable value by key. | ||
T get<T>(String key, {T? defaultValue}); | ||
T get<T>( | ||
String key, { | ||
T? defaultValue, | ||
T Function(String value)? parser, | ||
String decryptionKey = '', | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export 'env.dart'; | ||
export 'env_loader.dart'; | ||
export 'base_env_loader.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,114 +1,56 @@ | ||
import 'package:flutter/services.dart'; | ||
|
||
import 'base_env.dart'; | ||
import 'cipher.dart'; | ||
|
||
/// [Env] class is a singleton class that provides access to environment variables. | ||
class Env extends BaseEnv { | ||
/// Returns the singleton instance of [Env]. | ||
factory Env() => _instance; | ||
|
||
Env._(); | ||
import 'base_env_loader.dart'; | ||
import 'env_loader.dart'; | ||
|
||
/// This class is used to load environment variables from a .env file | ||
class Env { | ||
/// The singleton instance of [EnvLoader]. | ||
factory Env() { | ||
_instance ??= Env._internal(); | ||
return _instance!; | ||
} | ||
|
||
static final Env _instance = Env._(); | ||
Env._internal(); | ||
|
||
bool _isInitialized = false; | ||
static Env? _instance; | ||
|
||
final Map<String, dynamic> _entries = <String, dynamic>{}; | ||
/// The environment variables provider. | ||
BaseEnvLoader? _env; | ||
|
||
@override | ||
bool get isInitialized => _isInitialized; | ||
/// The instance of [BaseEnv]. | ||
BaseEnvLoader? get env => _env; | ||
|
||
@override | ||
Map<String, dynamic> get entries { | ||
_checkInitialization(); | ||
/// Returns `true` if [Env] is initialized, otherwise `false`. | ||
bool get isInitialized => _env?.isInitialized ?? false; | ||
|
||
return _entries; | ||
/// Initializes [EnvLoader] with an instance of [BaseEnv]. | ||
/// Loads environment variables from a `.env` file. | ||
/// | ||
/// If [filename] is not provided, it will default to `.env`. | ||
Future<void> initialize(BaseEnvLoader env) async { | ||
_env = env; | ||
return _env!.loadEnvironment(); | ||
} | ||
|
||
@override | ||
Future<void> load([String filename = '.env']) async { | ||
_entries.clear(); | ||
|
||
final List<String> fileEntries = await _getEntriesFromFile(filename); | ||
|
||
for (final String entry in fileEntries) { | ||
final List<String> items = entry.split('='); | ||
|
||
if (items.length > 1) { | ||
_entries[items.first.trim()] = items.sublist(1).join('=').trim(); | ||
} | ||
} | ||
|
||
_isInitialized = true; | ||
/// Loads environment variables from a `.env` file. | ||
@Deprecated('Use initialize() method instead.') | ||
Future<void> load([String filePath = '.env']) async { | ||
_env ??= EnvLoader(filePath: filePath); | ||
return _env!.loadEnvironment(); | ||
} | ||
|
||
@override | ||
/// Retrieves an environment variable value by key. | ||
T get<T>( | ||
String key, { | ||
T? defaultValue, | ||
T Function(String value)? parser, | ||
String decryptionKey = '', | ||
}) { | ||
_checkInitialization(); | ||
|
||
if (!_entries.containsKey(key)) { | ||
if (defaultValue == null) { | ||
throw Exception('$runtimeType does not contain a value for key: $key.'); | ||
} | ||
|
||
return defaultValue; | ||
} | ||
|
||
final String value = decryptionKey.isEmpty | ||
? _entries[key] | ||
: Cipher().decrypt(message: _entries[key], key: decryptionKey); | ||
|
||
if (parser != null) { | ||
return parser(value); | ||
} | ||
|
||
switch (T) { | ||
case int: | ||
return int.tryParse(value) as T; | ||
case double: | ||
return double.tryParse(value) as T; | ||
case bool: | ||
return (value.toLowerCase() == 'true') as T; | ||
|
||
default: | ||
return value as T; | ||
} | ||
} | ||
|
||
Future<List<String>> _getEntriesFromFile(String filename) async { | ||
final String envFileContent = await rootBundle.loadString(filename); | ||
|
||
if (envFileContent.isEmpty) { | ||
throw Exception('$runtimeType: $filename is empty.'); | ||
} | ||
|
||
final List<String> entries = <String>[]; | ||
final List<String> content = envFileContent.split('\n'); | ||
|
||
for (final String line in content) { | ||
if (line.isEmpty || line.startsWith('#')) { | ||
continue; | ||
} | ||
|
||
entries.add(line); | ||
} | ||
|
||
return entries; | ||
} | ||
|
||
void _checkInitialization() { | ||
if (_isInitialized) { | ||
return; | ||
} | ||
|
||
throw Exception( | ||
'$runtimeType is not initialized, call load() method first.', | ||
); | ||
} | ||
}) => | ||
isInitialized | ||
? _env!.get<T>( | ||
key, | ||
defaultValue: defaultValue, | ||
parser: parser, | ||
decryptionKey: decryptionKey, | ||
) | ||
: throw Exception('EnvLoader is not initialized.'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import 'package:flutter/services.dart'; | ||
|
||
import 'base_env_loader.dart'; | ||
import 'cipher.dart'; | ||
|
||
/// [Env] class is a singleton class that provides access to environment variables. | ||
class EnvLoader extends BaseEnvLoader { | ||
/// Creates a new instance of [EnvLoader]. | ||
EnvLoader({required this.filePath}); | ||
|
||
/// The path to the file. | ||
final String filePath; | ||
|
||
bool _isInitialized = false; | ||
|
||
final Map<String, dynamic> _entries = <String, dynamic>{}; | ||
|
||
@override | ||
bool get isInitialized => _isInitialized; | ||
|
||
@override | ||
Map<String, dynamic> get entries { | ||
_checkInitialization(); | ||
|
||
return _entries; | ||
} | ||
|
||
@override | ||
Future<void> loadEnvironment() async { | ||
_entries.clear(); | ||
|
||
final List<String> fileEntries = await _getEntriesFromFile(filePath); | ||
|
||
for (final String entry in fileEntries) { | ||
final List<String> items = entry.split('='); | ||
|
||
if (items.length > 1) { | ||
_entries[items.first.trim()] = items.sublist(1).join('=').trim(); | ||
} | ||
} | ||
|
||
_isInitialized = true; | ||
} | ||
|
||
@override | ||
T get<T>( | ||
String key, { | ||
T? defaultValue, | ||
T Function(String value)? parser, | ||
String decryptionKey = '', | ||
}) { | ||
_checkInitialization(); | ||
|
||
if (!_entries.containsKey(key)) { | ||
if (defaultValue == null) { | ||
throw Exception('$runtimeType does not contain a value for key: $key.'); | ||
} | ||
|
||
return defaultValue; | ||
} | ||
|
||
final String value = decryptionKey.isEmpty | ||
? _entries[key] | ||
: Cipher().decrypt(message: _entries[key], key: decryptionKey); | ||
|
||
if (parser != null) { | ||
return parser(value); | ||
} | ||
|
||
switch (T) { | ||
case int: | ||
return int.tryParse(value) as T; | ||
case double: | ||
return double.tryParse(value) as T; | ||
case bool: | ||
if (value.toLowerCase() == 'true') { | ||
return true as T; | ||
} else if (value.toLowerCase() == 'false') { | ||
return false as T; | ||
} else { | ||
throw FormatException('Invalid boolean value: $value'); | ||
} | ||
|
||
default: | ||
return value as T; | ||
} | ||
} | ||
|
||
Future<List<String>> _getEntriesFromFile(String filename) async { | ||
final String envFileContent = await rootBundle.loadString(filename); | ||
|
||
if (envFileContent.isEmpty) { | ||
throw Exception('$runtimeType: $filename is empty.'); | ||
} | ||
|
||
final List<String> entries = <String>[]; | ||
final List<String> content = envFileContent.split('\n'); | ||
|
||
for (final String line in content) { | ||
if (line.isEmpty || line.startsWith('#')) { | ||
continue; | ||
} | ||
|
||
entries.add(line); | ||
} | ||
|
||
return entries; | ||
} | ||
|
||
void _checkInitialization() { | ||
if (_isInitialized) { | ||
return; | ||
} | ||
|
||
throw Exception( | ||
'$runtimeType is not initialized, call load() method first.', | ||
); | ||
} | ||
} |
Oops, something went wrong.