-
Notifications
You must be signed in to change notification settings - Fork 604
Ability to inherit auth credentials #917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ import 'package:apidash/consts.dart'; | |
| import 'package:apidash/providers/providers.dart'; | ||
| import 'package:apidash/utils/file_utils.dart'; | ||
| import 'package:apidash_core/apidash_core.dart'; | ||
| import 'package:better_networking/better_networking.dart'; | ||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
| import '../services/services.dart' show hiveHandler, HiveHandler; | ||
|
|
||
|
|
@@ -126,11 +127,13 @@ class EnvironmentsStateNotifier | |
| String id, { | ||
| String? name, | ||
| List<EnvironmentVariableModel>? values, | ||
| AuthModel? defaultAuthModel, | ||
| }) { | ||
| final environment = state![id]!; | ||
| final updatedEnvironment = environment.copyWith( | ||
| name: name ?? environment.name, | ||
| values: values ?? environment.values, | ||
| defaultAuthModel: defaultAuthModel ?? environment.defaultAuthModel, | ||
|
||
| ); | ||
| state = { | ||
| ...state!, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import 'package:apidash_design_system/apidash_design_system.dart'; | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:apidash_core/apidash_core.dart'; | ||
| import 'package:better_networking/better_networking.dart'; | ||
| import 'api_key_auth_fields.dart'; | ||
| import 'basic_auth_fields.dart'; | ||
| import 'bearer_auth_fields.dart'; | ||
|
|
@@ -12,16 +13,20 @@ import 'oauth2_field.dart'; | |
|
|
||
| class AuthPage extends StatelessWidget { | ||
| final AuthModel? authModel; | ||
| final AuthInheritanceType? authInheritanceType; | ||
| final bool readOnly; | ||
| final Function(APIAuthType? newType)? onChangedAuthType; | ||
| final Function(AuthModel? model)? updateAuthData; | ||
| final Function(AuthInheritanceType? newType)? onChangedAuthInheritanceType; | ||
|
|
||
| const AuthPage({ | ||
| super.key, | ||
| this.authModel, | ||
| this.authInheritanceType, | ||
| this.readOnly = false, | ||
| this.onChangedAuthType, | ||
| this.updateAuthData, | ||
| this.onChangedAuthInheritanceType, | ||
| }); | ||
|
|
||
| @override | ||
|
|
@@ -41,61 +46,79 @@ class AuthPage extends StatelessWidget { | |
| SizedBox( | ||
| height: 8, | ||
| ), | ||
| ADPopupMenu<APIAuthType>( | ||
| value: authModel?.type.displayType, | ||
| values: APIAuthType.values | ||
| .map((type) => (type, type.displayType)) | ||
| .toList(), | ||
| tooltip: kTooltipSelectAuth, | ||
| isOutlined: true, | ||
| onChanged: readOnly ? null : onChangedAuthType, | ||
| ), | ||
| const SizedBox(height: 48), | ||
| switch (authModel?.type) { | ||
| APIAuthType.basic => BasicAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.bearer => BearerAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| // Auth inheritance selection | ||
| if (!readOnly) ...[ | ||
| ADPopupMenu<AuthInheritanceType>( | ||
| value: (authInheritanceType ?? AuthInheritanceType.none).displayType, | ||
| values: AuthInheritanceType.values | ||
| .map((type) => (type, type.displayType)) | ||
| .toList(), | ||
| tooltip: "Select auth inheritance type", | ||
| isOutlined: true, | ||
| onChanged: onChangedAuthInheritanceType, | ||
| ), | ||
| const SizedBox(height: 16), | ||
|
Comment on lines
46
to
+60
|
||
| ], | ||
|
Comment on lines
+49
to
+61
|
||
| // Show auth type selection only when not inheriting | ||
| if (authInheritanceType != AuthInheritanceType.environment) ...[ | ||
| ADPopupMenu<APIAuthType>( | ||
| value: authModel?.type.displayType, | ||
| values: APIAuthType.values | ||
| .map((type) => (type, type.displayType)) | ||
| .toList(), | ||
| tooltip: kTooltipSelectAuth, | ||
| isOutlined: true, | ||
| onChanged: readOnly ? null : onChangedAuthType, | ||
| ), | ||
| const SizedBox(height: 48), | ||
| switch (authModel?.type) { | ||
| APIAuthType.basic => BasicAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.bearer => BearerAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.apiKey => ApiKeyAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| APIAuthType.apiKey => ApiKeyAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.jwt => JwtAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| APIAuthType.jwt => JwtAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.digest => DigestAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| APIAuthType.digest => DigestAuthFields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.oauth1 => OAuth1Fields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.oauth2 => OAuth2Fields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| APIAuthType.oauth2 => OAuth2Fields( | ||
| readOnly: readOnly, | ||
| authData: authModel, | ||
| updateAuth: updateAuthData, | ||
| ), | ||
| APIAuthType.none => | ||
| Text(readOnly ? kMsgNoAuth : kMsgNoAuthSelected), | ||
| _ => Text(readOnly | ||
| ? "${authModel?.type.name} $kMsgAuthNotSupported" | ||
| : kMsgNotImplemented), | ||
| } | ||
| APIAuthType.none => | ||
| Text(readOnly ? kMsgNoAuth : kMsgNoAuthSelected), | ||
| _ => Text(readOnly | ||
| ? "${authModel?.type.name} $kMsgAuthNotSupported" | ||
| : kMsgNotImplemented), | ||
| } | ||
| ] else ...[ | ||
| const Text("Using environment inherited authentication"), | ||
| ] | ||
| ], | ||
| ), | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import 'package:apidash_design_system/apidash_design_system.dart'; | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||
| import 'package:apidash/providers/providers.dart'; | ||
| import 'package:apidash/widgets/widgets.dart'; | ||
| import 'package:apidash/consts.dart'; | ||
| import 'package:better_networking/better_networking.dart'; | ||
| import '../common_widgets/common_widgets.dart'; | ||
|
|
||
| class EnvironmentAuthEditor extends ConsumerWidget { | ||
| const EnvironmentAuthEditor({super.key}); | ||
|
|
||
| @override | ||
| Widget build(BuildContext context, WidgetRef ref) { | ||
| final environment = ref.watch(selectedEnvironmentModelProvider); | ||
|
|
||
| if (environment == null) { | ||
| return const SizedBox.shrink(); | ||
| } | ||
|
|
||
| final defaultAuthModel = environment.defaultAuthModel; | ||
|
|
||
| return Padding( | ||
| padding: const EdgeInsets.all(16.0), | ||
| child: Column( | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ | ||
| const Text( | ||
| "Default Authentication", | ||
| style: TextStyle( | ||
| fontWeight: FontWeight.bold, | ||
| fontSize: 16, | ||
| ), | ||
| ), | ||
| const SizedBox(height: 16), | ||
| const Text( | ||
| "Set default authentication for all requests using this environment", | ||
| style: TextStyle( | ||
| fontSize: 14, | ||
| ), | ||
| ), | ||
| const SizedBox(height: 24), | ||
| AuthPage( | ||
| authModel: defaultAuthModel, | ||
| readOnly: false, | ||
| onChangedAuthType: (newType) { | ||
| if (newType != null) { | ||
| final updatedAuthModel = defaultAuthModel?.copyWith(type: newType) ?? | ||
| AuthModel(type: newType); | ||
| ref | ||
| .read(environmentsStateNotifierProvider.notifier) | ||
| .updateEnvironment( | ||
| environment.id, | ||
| values: environment.values, | ||
| defaultAuthModel: updatedAuthModel, | ||
| ); | ||
| } | ||
| }, | ||
| updateAuthData: (model) { | ||
| ref | ||
| .read(environmentsStateNotifierProvider.notifier) | ||
| .updateEnvironment( | ||
| environment.id, | ||
| values: environment.values, | ||
| defaultAuthModel: model, | ||
| ); | ||
| }, | ||
| ), | ||
| ], | ||
| ), | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import 'package:apidash/widgets/widgets.dart'; | |
| import 'package:apidash/consts.dart'; | ||
| import '../common_widgets/common_widgets.dart'; | ||
| import './editor_pane/variables_pane.dart'; | ||
| import 'environment_auth_editor.dart'; | ||
|
|
||
| class EnvironmentEditor extends ConsumerWidget { | ||
| const EnvironmentEditor({super.key}); | ||
|
|
@@ -15,6 +16,7 @@ class EnvironmentEditor extends ConsumerWidget { | |
| final id = ref.watch(selectedEnvironmentIdStateProvider); | ||
| final name = ref | ||
| .watch(selectedEnvironmentModelProvider.select((value) => value?.name)); | ||
|
|
||
| return Padding( | ||
| padding: context.isMediumWindow | ||
| ? kPb10 | ||
|
|
@@ -84,24 +86,44 @@ class EnvironmentEditor extends ConsumerWidget { | |
| borderRadius: kBorderRadius12, | ||
| ), | ||
| elevation: 0, | ||
| child: const Padding( | ||
| padding: kPv6, | ||
| child: DefaultTabController( | ||
| length: 2, | ||
| child: Column( | ||
| children: [ | ||
| kHSpacer40, | ||
| Row( | ||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
| children: [ | ||
| SizedBox(width: 30), | ||
| Text("Variable"), | ||
| SizedBox(width: 30), | ||
| Text("Value"), | ||
| SizedBox(width: 40), | ||
| TabBar( | ||
| tabs: const [ | ||
| Tab(text: "Variables"), | ||
| Tab(text: "Auth"), | ||
| ], | ||
| ), | ||
| kHSpacer40, | ||
| Divider(), | ||
| Expanded(child: EditEnvironmentVariables()) | ||
| const Expanded( | ||
| child: TabBarView( | ||
| children: [ | ||
| Padding( | ||
| padding: kPv6, | ||
| child: Column( | ||
| children: [ | ||
| kHSpacer40, | ||
| Row( | ||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
| children: [ | ||
| SizedBox(width: 30), | ||
| Text("Variable"), | ||
| SizedBox(width: 30), | ||
| Text("Value"), | ||
| SizedBox(width: 40), | ||
|
Comment on lines
+110
to
+114
|
||
| ], | ||
| ), | ||
| kHSpacer40, | ||
| Divider(), | ||
| Expanded(child: EditEnvironmentVariables()) | ||
| ], | ||
| ), | ||
| ), | ||
| EnvironmentAuthEditor(), | ||
| ], | ||
| ), | ||
| ), | ||
| ], | ||
| ), | ||
| ), | ||
|
|
@@ -112,4 +134,4 @@ class EnvironmentEditor extends ConsumerWidget { | |
| ), | ||
| ); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The auth inheritance logic in
getSubstitutedHttpRequestModellacks test coverage. Given the complexity of this feature and that it's a core part of the PR, tests should verify:authInheritanceTypeisenvironmentand environment hasdefaultAuthModelauthInheritanceTypeisnoneornulldefaultAuthModel