Skip to content
Open
27 changes: 0 additions & 27 deletions lib/dashbot/providers/dashbot_window_notifier.g.dart

This file was deleted.

14 changes: 14 additions & 0 deletions lib/providers/collection_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:apidash/terminal/terminal.dart';
import 'providers.dart';
import '../models/models.dart';
import '../services/services.dart';
import '../services/auth_resolution_service.dart';
import '../utils/utils.dart';

final selectedIdStateProvider = StateProvider<String?>((ref) => null);
Expand Down Expand Up @@ -353,6 +354,19 @@ class CollectionStateNotifier
executionRequestModel.httpRequestModel!);
}

// Resolve authentication following priority: Request > Environment > Global
final resolvedAuth = resolveAuth(
executionRequestModel,
ref.read(activeEnvironmentIdStateProvider),
ref.read(environmentsStateNotifierProvider),
);

// Apply resolved authentication to the request model
if (resolvedAuth != null) {
substitutedHttpRequestModel =
substitutedHttpRequestModel.copyWith(authModel: resolvedAuth);
}

// Terminal
final terminal = ref.read(terminalStateProvider.notifier);

Expand Down
3 changes: 3 additions & 0 deletions lib/providers/environment_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class EnvironmentsStateNotifier
id: environmentModelFromJson.id,
name: environmentModelFromJson.name,
values: environmentModelFromJson.values,
authModel: environmentModelFromJson.authModel,
);
environmentsMap[environmentId] = environmentModel;
}
Expand Down Expand Up @@ -126,11 +127,13 @@ class EnvironmentsStateNotifier
String id, {
String? name,
List<EnvironmentVariableModel>? values,
AuthModel? authModel,
}) {
final environment = state![id]!;
final updatedEnvironment = environment.copyWith(
name: name ?? environment.name,
values: values ?? environment.values,
authModel: authModel ?? environment.authModel,
);
state = {
...state!,
Expand Down
47 changes: 47 additions & 0 deletions lib/screens/envvar/editor_pane/auth_pane.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:apidash_core/apidash_core.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:apidash/providers/providers.dart';
import '../../common_widgets/common_widgets.dart';

class EditEnvironmentAuth extends ConsumerWidget {
const EditEnvironmentAuth({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedId = ref.watch(selectedEnvironmentIdStateProvider);
final selectedEnvironment = ref.watch(selectedEnvironmentModelProvider);
final currentAuthData = selectedEnvironment?.authModel;

if (selectedId == null) {
return const SizedBox.shrink();
}

return AuthPage(
authModel: currentAuthData,
readOnly: false,
onChangedAuthType: (newType) {
if (newType != null) {
ref.read(environmentsStateNotifierProvider.notifier).updateEnvironment(
selectedId,
authModel: currentAuthData?.copyWith(type: newType) ??
AuthModel(type: newType),
);
}
},
updateAuthData: (model) {
if (model == null) {
ref.read(environmentsStateNotifierProvider.notifier).updateEnvironment(
selectedId,
authModel: AuthModel(type: APIAuthType.none),
);
} else {
ref.read(environmentsStateNotifierProvider.notifier).updateEnvironment(
selectedId,
authModel: model,
);
}
},
);
}
}
11 changes: 10 additions & 1 deletion lib/screens/envvar/editor_pane/variables_tabs.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:apidash_core/apidash_core.dart';
import 'package:apidash_design_system/apidash_design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand All @@ -7,6 +8,7 @@ import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/utils/utils.dart';
import 'variables_pane.dart';
import 'secrets_pane.dart';
import 'auth_pane.dart';

class VariablesTabs extends StatefulHookConsumerWidget {
const VariablesTabs({super.key});
Expand All @@ -19,14 +21,16 @@ class _VariablesTabsState extends ConsumerState<VariablesTabs>
with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final tabController = useTabController(initialLength: 2, vsync: this);
final tabController = useTabController(initialLength: 3, vsync: this);
final selectedEnvironment = ref.watch(selectedEnvironmentModelProvider);
final variablesLength =
getEnvironmentVariables(selectedEnvironment, removeEmptyModels: true)
.length;
final secretsLength =
getEnvironmentSecrets(selectedEnvironment, removeEmptyModels: true)
.length;
final hasAuth = selectedEnvironment?.authModel != null &&
selectedEnvironment?.authModel?.type != APIAuthType.none;
return Column(
children: [
!context.isMediumWindow ? kVSpacer10 : const SizedBox.shrink(),
Expand All @@ -43,6 +47,10 @@ class _VariablesTabsState extends ConsumerState<VariablesTabs>
text: 'Secrets',
showIndicator: secretsLength > 0,
),
TabLabel(
text: 'Authentication',
showIndicator: hasAuth,
),
],
),
kVSpacer5,
Expand All @@ -53,6 +61,7 @@ class _VariablesTabsState extends ConsumerState<VariablesTabs>
children: const [
EditEnvironmentVariables(),
EditEnvironmentSecrets(),
EditEnvironmentAuth(),
],
),
),
Expand Down
21 changes: 2 additions & 19 deletions lib/screens/envvar/environment_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/consts.dart';
import '../common_widgets/common_widgets.dart';
import './editor_pane/variables_pane.dart';
import './editor_pane/variables_tabs.dart';

class EnvironmentEditor extends ConsumerWidget {
const EnvironmentEditor({super.key});
Expand Down Expand Up @@ -86,24 +86,7 @@ class EnvironmentEditor extends ConsumerWidget {
elevation: 0,
child: const 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),
],
),
kHSpacer40,
Divider(),
Expanded(child: EditEnvironmentVariables())
],
),
child: VariablesTabs(),
),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash_core/apidash_core.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/services/auth_resolution_service.dart';
import '../../../../common_widgets/common_widgets.dart';

class EditAuthType extends ConsumerWidget {
Expand All @@ -14,40 +16,147 @@ class EditAuthType extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedRequest = ref.read(selectedRequestModelProvider);
final selectedRequest = ref.watch(selectedRequestModelProvider);
if (selectedRequest == null) {
return const SizedBox.shrink();
}

ref.watch(
selectedRequestModelProvider.select((request) =>
request?.httpRequestModel?.authModel?.type ?? APIAuthType.none),
final requestAuth = selectedRequest.httpRequestModel?.authModel;
final hasRequestAuth =
requestAuth != null && requestAuth.type != APIAuthType.none;

// Get resolved auth to determine inheritance source
final activeEnvironmentId = ref.watch(activeEnvironmentIdStateProvider);
final environments = ref.watch(environmentsStateNotifierProvider);
final resolvedAuth = resolveAuth(
selectedRequest,
activeEnvironmentId,
environments,
);
final currentAuthData = selectedRequest.httpRequestModel?.authModel;

return AuthPage(
authModel: currentAuthData,
readOnly: readOnly,
onChangedAuthType: (newType) {
final selectedRequest = ref.read(selectedRequestModelProvider);
if (newType != null) {
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: selectedRequest?.httpRequestModel?.authModel
?.copyWith(type: newType) ??
AuthModel(type: newType),
);
}
},
updateAuthData: (model) {
if (model == null) {
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: AuthModel(type: APIAuthType.none),
);
}
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: model,
);
},
// Determine inheritance source
String? inheritanceSource;
if (!hasRequestAuth && resolvedAuth != null && environments != null) {
if (activeEnvironmentId != null &&
activeEnvironmentId.isNotEmpty &&
environments.containsKey(activeEnvironmentId) &&
environments[activeEnvironmentId]?.authModel != null &&
environments[activeEnvironmentId]!.authModel!.type !=
APIAuthType.none) {
final envName = environments[activeEnvironmentId]?.name ?? 'Environment';
inheritanceSource = '$envName Environment';
} else if (environments.containsKey(kGlobalEnvironmentId) &&
environments[kGlobalEnvironmentId]?.authModel != null &&
environments[kGlobalEnvironmentId]!.authModel!.type !=
APIAuthType.none) {
inheritanceSource = 'Global';
}
}

return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (inheritanceSource != null && !hasRequestAuth)
Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
),
),
child: Row(
children: [
Icon(
Icons.info_outline,
size: 20,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 8),
Expanded(
child: Text(
'Inherited from: $inheritanceSource',
style: Theme.of(context).textTheme.bodySmall,
),
),
if (!readOnly)
TextButton(
onPressed: () {
// Override with request-level auth
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: resolvedAuth,
);
},
child: const Text('Override'),
),
],
),
),
if (hasRequestAuth && inheritanceSource != null)
Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.3),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(
Icons.lock_outline,
size: 16,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 8),
Text(
'Overridden',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w500,
),
),
const Spacer(),
if (!readOnly)
TextButton(
onPressed: () {
// Clear request-level auth to use inherited
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: AuthModel(type: APIAuthType.none),
);
},
child: const Text('Use Inherited'),
),
],
),
),
AuthPage(
authModel: hasRequestAuth ? requestAuth : resolvedAuth,
readOnly: readOnly,
onChangedAuthType: (newType) {
final selectedRequest = ref.read(selectedRequestModelProvider);
if (newType != null) {
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: selectedRequest?.httpRequestModel?.authModel
?.copyWith(type: newType) ??
AuthModel(type: newType),
);
}
},
updateAuthData: (model) {
if (model == null) {
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: AuthModel(type: APIAuthType.none),
);
} else {
ref.read(collectionStateNotifierProvider.notifier).update(
authModel: model,
);
}
},
),
],
);
}
}
Loading