feat: add environment variable substitution support for auth models

This commit is contained in:
Udhay-Adithya
2025-07-19 01:20:57 +05:30
parent 572688cb3b
commit cc8abee296
9 changed files with 318 additions and 150 deletions

View File

@@ -317,7 +317,7 @@ class CollectionStateNotifier
var responseRec = await sendHttpRequest( var responseRec = await sendHttpRequest(
requestId, requestId,
apiType, apiType,
requestModel.httpRequestModel?.authModel, substitutedHttpRequestModel.authModel,
substitutedHttpRequestModel, substitutedHttpRequestModel,
defaultUriScheme: defaultUriScheme, defaultUriScheme: defaultUriScheme,
noSSL: noSSL, noSSL: noSSL,

View File

@@ -20,17 +20,16 @@ class ApiKeyAuthFields extends StatefulWidget {
} }
class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> { class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
late TextEditingController _keyController; late String _key;
late TextEditingController _nameController; late String _name;
late String _addKeyTo; late String _addKeyTo;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final apiAuth = widget.authData?.apikey; final apiAuth = widget.authData?.apikey;
_keyController = TextEditingController(text: apiAuth?.key ?? ''); _key = apiAuth?.key ?? '';
_nameController = _name = apiAuth?.name ?? kApiKeyHeaderName;
TextEditingController(text: apiAuth?.name ?? kApiKeyHeaderName);
_addKeyTo = apiAuth?.location ?? kAddToDefaultLocation; _addKeyTo = apiAuth?.location ?? kAddToDefaultLocation;
} }
@@ -66,20 +65,26 @@ class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
}, },
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _nameController,
hintText: kHintTextFieldName, hintText: kHintTextFieldName,
onChanged: (value) => _updateApiKeyAuth(), initialValue: widget.authData?.apikey?.name,
onChanged: (value) {
_name = value;
_updateApiKeyAuth();
},
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _keyController,
title: kLabelApiKey, title: kLabelApiKey,
hintText: kHintTextKey, hintText: kHintTextKey,
isObscureText: true, isObscureText: true,
onChanged: (value) => _updateApiKeyAuth(), initialValue: widget.authData?.apikey?.key,
onChanged: (value) {
_key = value;
_updateApiKeyAuth();
},
), ),
], ],
); );
@@ -87,8 +92,8 @@ class _ApiKeyAuthFieldsState extends State<ApiKeyAuthFields> {
void _updateApiKeyAuth() { void _updateApiKeyAuth() {
final apiKey = AuthApiKeyModel( final apiKey = AuthApiKeyModel(
key: _keyController.text.trim(), key: _key.trim(),
name: _nameController.text.trim(), name: _name.trim(),
location: _addKeyTo, location: _addKeyTo,
); );
widget.updateAuth?.call(widget.authData?.copyWith( widget.updateAuth?.call(widget.authData?.copyWith(

View File

@@ -3,7 +3,7 @@ import 'package:apidash_core/apidash_core.dart';
import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/widgets/widgets.dart';
import 'consts.dart'; import 'consts.dart';
class BasicAuthFields extends StatelessWidget { class BasicAuthFields extends StatefulWidget {
final AuthModel? authData; final AuthModel? authData;
final Function(AuthModel?)? updateAuth; final Function(AuthModel?)? updateAuth;
final bool readOnly; final bool readOnly;
@@ -16,50 +16,55 @@ class BasicAuthFields extends StatelessWidget {
}); });
@override @override
Widget build(BuildContext context) { State<BasicAuthFields> createState() => _BasicAuthFieldsState();
final usernameController = TextEditingController( }
text: authData?.basic?.username ?? '',
);
final passwordController = TextEditingController(
text: authData?.basic?.password ?? '',
);
class _BasicAuthFieldsState extends State<BasicAuthFields> {
late String _username;
late String _password;
@override
void initState() {
super.initState();
_username = widget.authData?.basic?.username ?? '';
_password = widget.authData?.basic?.password ?? '';
}
@override
Widget build(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AuthTextField( EnvAuthField(
readOnly: readOnly, readOnly: widget.readOnly,
hintText: kHintUsername, hintText: kHintUsername,
controller: usernameController, initialValue: widget.authData?.basic?.username,
onChanged: (_) => _updateBasicAuth( onChanged: (value) {
usernameController, _username = value;
passwordController, _updateBasicAuth();
), },
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
AuthTextField( EnvAuthField(
readOnly: readOnly, readOnly: widget.readOnly,
hintText: kHintPassword, hintText: kHintPassword,
isObscureText: true, isObscureText: true,
controller: passwordController, initialValue: widget.authData?.basic?.password,
onChanged: (_) => _updateBasicAuth( onChanged: (value) {
usernameController, _password = value;
passwordController, _updateBasicAuth();
), },
), ),
], ],
); );
} }
void _updateBasicAuth( void _updateBasicAuth() {
TextEditingController usernameController,
TextEditingController passwordController,
) {
final basicAuth = AuthBasicAuthModel( final basicAuth = AuthBasicAuthModel(
username: usernameController.text.trim(), username: _username.trim(),
password: passwordController.text.trim(), password: _password.trim(),
); );
updateAuth?.call(authData?.copyWith( widget.updateAuth?.call(widget.authData?.copyWith(
type: APIAuthType.basic, type: APIAuthType.basic,
basic: basicAuth, basic: basicAuth,
) ?? ) ??

View File

@@ -20,29 +20,31 @@ class BearerAuthFields extends StatefulWidget {
} }
class _BearerAuthFieldsState extends State<BearerAuthFields> { class _BearerAuthFieldsState extends State<BearerAuthFields> {
late TextEditingController _tokenController; late String _token;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final bearerAuth = widget.authData?.bearer; _token = widget.authData?.bearer?.token ?? '';
_tokenController = TextEditingController(text: bearerAuth?.token ?? '');
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AuthTextField( return EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _tokenController,
hintText: kHintToken, hintText: kHintToken,
isObscureText: true, isObscureText: true,
onChanged: (value) => _updateBearerAuth(), initialValue: widget.authData?.bearer?.token,
onChanged: (value) {
_token = value;
_updateBearerAuth();
},
); );
} }
void _updateBearerAuth() { void _updateBearerAuth() {
final bearer = AuthBearerModel( final bearer = AuthBearerModel(
token: _tokenController.text.trim(), token: _token.trim(),
); );
widget.updateAuth?.call(widget.authData?.copyWith( widget.updateAuth?.call(widget.authData?.copyWith(
type: APIAuthType.bearer, type: APIAuthType.bearer,

View File

@@ -21,25 +21,25 @@ class DigestAuthFields extends StatefulWidget {
} }
class _DigestAuthFieldsState extends State<DigestAuthFields> { class _DigestAuthFieldsState extends State<DigestAuthFields> {
late TextEditingController _usernameController; late String _username;
late TextEditingController _passwordController; late String _password;
late TextEditingController _realmController; late String _realm;
late TextEditingController _nonceController; late String _nonce;
late String _algorithmController; late String _algorithmController;
late TextEditingController _qopController; late String _qop;
late TextEditingController _opaqueController; late String _opaque;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final digest = widget.authData?.digest; final digest = widget.authData?.digest;
_usernameController = TextEditingController(text: digest?.username ?? ''); _username = digest?.username ?? '';
_passwordController = TextEditingController(text: digest?.password ?? ''); _password = digest?.password ?? '';
_realmController = TextEditingController(text: digest?.realm ?? ''); _realm = digest?.realm ?? '';
_nonceController = TextEditingController(text: digest?.nonce ?? ''); _nonce = digest?.nonce ?? '';
_algorithmController = digest?.algorithm ?? kDigestAlgos[0]; _algorithmController = digest?.algorithm ?? kDigestAlgos[0];
_qopController = TextEditingController(text: digest?.qop ?? kQop[0]); _qop = digest?.qop ?? kQop[0];
_opaqueController = TextEditingController(text: digest?.opaque ?? ''); _opaque = digest?.opaque ?? '';
} }
@override @override
@@ -48,37 +48,49 @@ class _DigestAuthFieldsState extends State<DigestAuthFields> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _usernameController,
hintText: kHintUsername, hintText: kHintUsername,
infoText: kInfoDigestUsername, infoText: kInfoDigestUsername,
onChanged: (_) => _updateDigestAuth(), initialValue: widget.authData?.digest?.username,
onChanged: (value) {
_username = value;
_updateDigestAuth();
},
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _passwordController,
hintText: kHintPassword, hintText: kHintPassword,
isObscureText: true, isObscureText: true,
infoText: kInfoDigestPassword, infoText: kInfoDigestPassword,
onChanged: (_) => _updateDigestAuth(), initialValue: widget.authData?.digest?.password,
onChanged: (value) {
_password = value;
_updateDigestAuth();
},
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _realmController,
hintText: kHintRealm, hintText: kHintRealm,
infoText: kInfoDigestRealm, infoText: kInfoDigestRealm,
onChanged: (_) => _updateDigestAuth(), initialValue: widget.authData?.digest?.realm,
onChanged: (value) {
_realm = value;
_updateDigestAuth();
},
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _nonceController,
hintText: kHintNonce, hintText: kHintNonce,
infoText: kInfoDigestNonce, infoText: kInfoDigestNonce,
onChanged: (_) => _updateDigestAuth(), initialValue: widget.authData?.digest?.nonce,
onChanged: (value) {
_nonce = value;
_updateDigestAuth();
},
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
@@ -106,20 +118,26 @@ class _DigestAuthFieldsState extends State<DigestAuthFields> {
}, },
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _qopController,
hintText: kHintQop, hintText: kHintQop,
infoText: kInfoDigestQop, infoText: kInfoDigestQop,
onChanged: (_) => _updateDigestAuth(), initialValue: widget.authData?.digest?.qop,
onChanged: (value) {
_qop = value;
_updateDigestAuth();
},
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _opaqueController,
hintText: kHintDataString, hintText: kHintDataString,
infoText: kInfoDigestDataString, infoText: kInfoDigestDataString,
onChanged: (_) => _updateDigestAuth(), initialValue: widget.authData?.digest?.opaque,
onChanged: (value) {
_opaque = value;
_updateDigestAuth();
},
), ),
], ],
), ),
@@ -128,13 +146,13 @@ class _DigestAuthFieldsState extends State<DigestAuthFields> {
void _updateDigestAuth() { void _updateDigestAuth() {
final digest = AuthDigestModel( final digest = AuthDigestModel(
username: _usernameController.text.trim(), username: _username.trim(),
password: _passwordController.text.trim(), password: _password.trim(),
realm: _realmController.text.trim(), realm: _realm.trim(),
nonce: _nonceController.text.trim(), nonce: _nonce.trim(),
algorithm: _algorithmController.trim(), algorithm: _algorithmController.trim(),
qop: _qopController.text.trim(), qop: _qop.trim(),
opaque: _opaqueController.text.trim(), opaque: _opaque.trim(),
); );
widget.updateAuth?.call(widget.authData?.copyWith( widget.updateAuth?.call(widget.authData?.copyWith(
type: APIAuthType.digest, type: APIAuthType.digest,

View File

@@ -21,7 +21,7 @@ class JwtAuthFields extends StatefulWidget {
} }
class _JwtAuthFieldsState extends State<JwtAuthFields> { class _JwtAuthFieldsState extends State<JwtAuthFields> {
late TextEditingController _secretController; late String _secret;
late TextEditingController _privateKeyController; late TextEditingController _privateKeyController;
late TextEditingController _payloadController; late TextEditingController _payloadController;
late String _addTokenTo; late String _addTokenTo;
@@ -32,7 +32,7 @@ class _JwtAuthFieldsState extends State<JwtAuthFields> {
void initState() { void initState() {
super.initState(); super.initState();
final jwt = widget.authData?.jwt; final jwt = widget.authData?.jwt;
_secretController = TextEditingController(text: jwt?.secret ?? ''); _secret = jwt?.secret ?? '';
_privateKeyController = TextEditingController(text: jwt?.privateKey ?? ''); _privateKeyController = TextEditingController(text: jwt?.privateKey ?? '');
_payloadController = TextEditingController(text: jwt?.payload ?? ''); _payloadController = TextEditingController(text: jwt?.payload ?? '');
_addTokenTo = jwt?.addTokenTo ?? kAddToDefaultLocation; _addTokenTo = jwt?.addTokenTo ?? kAddToDefaultLocation;
@@ -96,13 +96,16 @@ class _JwtAuthFieldsState extends State<JwtAuthFields> {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
if (_algorithm.startsWith(kStartAlgo)) ...[ if (_algorithm.startsWith(kStartAlgo)) ...[
AuthTextField( EnvAuthField(
readOnly: widget.readOnly, readOnly: widget.readOnly,
controller: _secretController,
isObscureText: true, isObscureText: true,
hintText: kHintSecret, hintText: kHintSecret,
infoText: kInfoSecret, infoText: kInfoSecret,
onChanged: (value) => _updateJwtAuth(), initialValue: widget.authData?.jwt?.secret,
onChanged: (value) {
_secret = value;
_updateJwtAuth();
},
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
CheckboxListTile( CheckboxListTile(
@@ -207,7 +210,7 @@ class _JwtAuthFieldsState extends State<JwtAuthFields> {
void _updateJwtAuth() { void _updateJwtAuth() {
final jwt = AuthJwtModel( final jwt = AuthJwtModel(
secret: _secretController.text.trim(), secret: _secret.trim(),
privateKey: _privateKeyController.text.trim(), privateKey: _privateKeyController.text.trim(),
payload: _payloadController.text.trim(), payload: _payloadController.text.trim(),
addTokenTo: _addTokenTo, addTokenTo: _addTokenTo,

View File

@@ -91,10 +91,91 @@ HttpRequestModel substituteHttpRequestModel(
); );
}).toList(), }).toList(),
body: substituteVariables(httpRequestModel.body, combinedEnvVarMap), body: substituteVariables(httpRequestModel.body, combinedEnvVarMap),
authModel:
_substituteAuthModel(httpRequestModel.authModel, combinedEnvVarMap),
); );
return newRequestModel; return newRequestModel;
} }
AuthModel? _substituteAuthModel(
AuthModel? authModel, Map<String, String> envVarMap) {
if (authModel == null) return null;
switch (authModel.type) {
case APIAuthType.basic:
if (authModel.basic != null) {
final basic = authModel.basic!;
return authModel.copyWith(
basic: basic.copyWith(
username: substituteVariables(basic.username, envVarMap) ??
basic.username,
password: substituteVariables(basic.password, envVarMap) ??
basic.password,
),
);
}
break;
case APIAuthType.bearer:
if (authModel.bearer != null) {
final bearer = authModel.bearer!;
return authModel.copyWith(
bearer: bearer.copyWith(
token: substituteVariables(bearer.token, envVarMap) ?? bearer.token,
),
);
}
break;
case APIAuthType.apiKey:
if (authModel.apikey != null) {
final apiKey = authModel.apikey!;
return authModel.copyWith(
apikey: apiKey.copyWith(
key: substituteVariables(apiKey.key, envVarMap) ?? apiKey.key,
name: substituteVariables(apiKey.name, envVarMap) ?? apiKey.name,
),
);
}
break;
case APIAuthType.jwt:
if (authModel.jwt != null) {
final jwt = authModel.jwt!;
return authModel.copyWith(
jwt: jwt.copyWith(
secret: substituteVariables(jwt.secret, envVarMap) ?? jwt.secret,
privateKey: substituteVariables(jwt.privateKey, envVarMap) ??
jwt.privateKey,
payload: substituteVariables(jwt.payload, envVarMap) ?? jwt.payload,
),
);
}
break;
case APIAuthType.digest:
if (authModel.digest != null) {
final digest = authModel.digest!;
return authModel.copyWith(
digest: digest.copyWith(
username: substituteVariables(digest.username, envVarMap) ??
digest.username,
password: substituteVariables(digest.password, envVarMap) ??
digest.password,
realm: substituteVariables(digest.realm, envVarMap) ?? digest.realm,
nonce: substituteVariables(digest.nonce, envVarMap) ?? digest.nonce,
qop: substituteVariables(digest.qop, envVarMap) ?? digest.qop,
opaque:
substituteVariables(digest.opaque, envVarMap) ?? digest.opaque,
),
);
}
break;
case APIAuthType.oauth1:
case APIAuthType.oauth2:
case APIAuthType.none:
break;
}
return authModel;
}
List<EnvironmentVariableSuggestion>? getEnvironmentTriggerSuggestions( List<EnvironmentVariableSuggestion>? getEnvironmentTriggerSuggestions(
String query, String query,
Map<String, List<EnvironmentVariableModel>> envMap, Map<String, List<EnvironmentVariableModel>> envMap,

View File

@@ -1,44 +1,33 @@
import 'dart:math';
import 'package:apidash/consts.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:apidash_design_system/apidash_design_system.dart';
import '../screens/common_widgets/env_trigger_field.dart';
class AuthTextField extends StatefulWidget { class EnvAuthField extends StatefulWidget {
final String hintText; final String hintText;
final String? title; final String? title;
final TextEditingController controller;
final bool isObscureText; final bool isObscureText;
final Function(String)? onChanged; final Function(String)? onChanged;
final bool readOnly; final bool readOnly;
final String? infoText; final String? infoText;
final String? initialValue;
const AuthTextField( const EnvAuthField(
{super.key, {super.key,
this.title, this.title,
required this.hintText, required this.hintText,
required this.controller,
required this.onChanged, required this.onChanged,
this.readOnly = false, this.readOnly = false,
this.isObscureText = false, this.isObscureText = false,
this.infoText}); this.infoText,
this.initialValue});
@override @override
State<AuthTextField> createState() => _AuthFieldState(); State<EnvAuthField> createState() => _AuthFieldState();
}
class _AuthFieldState extends State<AuthTextField> {
late bool _obscureText;
@override
void initState() {
super.initState();
_obscureText = widget.isObscureText;
}
void _toggleVisibility() {
setState(() {
_obscureText = !_obscureText;
});
} }
class _AuthFieldState extends State<EnvAuthField> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AutofillGroup( return AutofillGroup(
@@ -67,49 +56,20 @@ class _AuthFieldState extends State<AuthTextField> {
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
TextFormField( EnvironmentTriggerField(
readOnly: widget.readOnly, keyId: "auth-${widget.title ?? widget.hintText}-${Random.secure()}",
controller: widget.controller, onChanged: widget.readOnly ? null : widget.onChanged,
initialValue: widget.initialValue,
style: kCodeStyle.copyWith( style: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.onSurface, color: Theme.of(context).colorScheme.onSurface,
fontSize: Theme.of(context).textTheme.bodyMedium?.fontSize, fontSize: Theme.of(context).textTheme.bodyMedium?.fontSize,
), ),
decoration: InputDecoration( decoration: getTextFieldInputDecoration(
filled: true, Theme.of(context).colorScheme,
fillColor: Theme.of(context).colorScheme.surfaceContainerLowest,
constraints: BoxConstraints(
maxWidth: MediaQuery.sizeOf(context).width - 80),
contentPadding: kP10,
hintText: widget.hintText, hintText: widget.hintText,
hintStyle: Theme.of(context).textTheme.bodySmall, isDense: true,
suffixIcon: widget.isObscureText contentPadding: kIsMobile ? kPh6b12 : null,
? IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
size: 20,
), ),
onPressed: _toggleVisibility,
)
: null,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.outline,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
),
),
),
validator: (value) {
if (value!.isEmpty) {
return "${widget.hintText} cannot be empty!";
}
return null;
},
obscureText: _obscureText,
onChanged: widget.onChanged,
), ),
], ],
), ),

View File

@@ -357,4 +357,98 @@ void main() {
expect(getVariableStatus(query, envMap, activeEnvironmentId), expected); expect(getVariableStatus(query, envMap, activeEnvironmentId), expected);
}); });
}); });
group("Testing auth model environment variable substitution", () {
test("Testing basic auth with environment variables", () {
const httpRequestModel = HttpRequestModel(
url: "{{url}}/test",
authModel: AuthModel(
type: APIAuthType.basic,
basic: AuthBasicAuthModel(
username: "{{basic_username}}admin",
password: "{{token}}pass",
),
),
);
Map<String?, List<EnvironmentVariableModel>> envMap = {
kGlobalEnvironmentId: [
EnvironmentVariableModel(key: "url", value: "api.apidash.dev"),
EnvironmentVariableModel(key: "basic_username", value: "testuser"),
EnvironmentVariableModel(key: "token", value: "secret"),
],
};
const activeEnvironmentId = null;
final result = substituteHttpRequestModel(
httpRequestModel,
envMap,
activeEnvironmentId,
);
expect(result.authModel?.basic?.username, "testuseradmin");
expect(result.authModel?.basic?.password, "secretpass");
expect(result.url, "api.apidash.dev/test");
});
test("Testing bearer auth with environment variables", () {
const httpRequestModel = HttpRequestModel(
url: "{{url}}/test",
authModel: AuthModel(
type: APIAuthType.bearer,
bearer: AuthBearerModel(
token: "{{bearer_token}}",
),
),
);
Map<String?, List<EnvironmentVariableModel>> envMap = {
kGlobalEnvironmentId: [
EnvironmentVariableModel(key: "url", value: "api.apidash.dev"),
EnvironmentVariableModel(key: "bearer_token", value: "secret123"),
],
};
const activeEnvironmentId = null;
final result = substituteHttpRequestModel(
httpRequestModel,
envMap,
activeEnvironmentId,
);
expect(result.authModel?.bearer?.token, "secret123");
});
test("Testing API key auth with environment variables", () {
const httpRequestModel = HttpRequestModel(
url: "{{url}}/test",
authModel: AuthModel(
type: APIAuthType.apiKey,
apikey: AuthApiKeyModel(
key: "{{api_key}}",
name: "{{header_name}}",
location: "header",
),
),
);
Map<String?, List<EnvironmentVariableModel>> envMap = {
kGlobalEnvironmentId: [
EnvironmentVariableModel(key: "url", value: "api.apidash.dev"),
EnvironmentVariableModel(key: "api_key", value: "key123"),
EnvironmentVariableModel(key: "header_name", value: "X-API-Key"),
],
};
const activeEnvironmentId = null;
final result = substituteHttpRequestModel(
httpRequestModel,
envMap,
activeEnvironmentId,
);
expect(result.authModel?.apikey?.key, "key123");
expect(result.authModel?.apikey?.name, "X-API-Key");
});
});
} }