fix: base url creation for openapi spec

This commit is contained in:
Udhay-Adithya
2025-09-27 20:56:30 +05:30
parent d2a5a774df
commit 15821a06ea
3 changed files with 162 additions and 41 deletions

View File

@@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:openapi_spec/openapi_spec.dart';
@@ -55,6 +57,11 @@ class DashbotImportNowButton extends ConsumerWidget with DashbotActionMixin {
method: s.method,
op: s.op,
);
log("SorceName: $sourceName");
payload['sourceName'] =
(sourceName != null && sourceName.trim().isNotEmpty)
? sourceName
: spec.info.title;
await chatNotifier.applyAutoFix(ChatAction.fromJson({
'action': 'apply_openapi',
'actionType': 'apply_openapi',

View File

@@ -65,4 +65,110 @@ class UrlEnvService {
final normalized = path.startsWith('/') ? path : '/$path';
return '{{$key}}$normalized';
}
/// Ensure (or create) an environment variable for a base URL coming from an
/// OpenAPI spec import. If the spec had no concrete host (thus parsing the
/// base URL yields no host) we derive a key using the first word of the spec
/// title to avoid every unrelated spec collapsing to BASE_URL_API.
///
/// Behaviour:
/// - If [baseUrl] is empty: returns a derived key `BASE_URL_<TITLEWORD>` but
/// does NOT create an env var (there is no value to store yet).
/// - If [baseUrl] has a host: behaves like [ensureBaseUrlEnv]. If the host
/// itself cannot be determined, it substitutes the first title word slug.
Future<String> ensureBaseUrlEnvForOpenApi(
String baseUrl, {
required String title,
required Map<String, EnvironmentModel>? Function() readEnvs,
required String? Function() readActiveEnvId,
required void Function(String id, {List<EnvironmentVariableModel>? values})
updateEnv,
}) async {
// Derive slug from title's first word upfront (used as fallback)
final titleSlug = _slugFromOpenApiTitleFirstWord(title);
final trimmedBase = baseUrl.trim();
final isTrivial = trimmedBase.isEmpty ||
trimmedBase == '/' ||
// path-only or variable server (no scheme and no host component)
(!trimmedBase.startsWith('http://') &&
!trimmedBase.startsWith('https://') &&
!trimmedBase.contains('://'));
if (isTrivial) {
final key = 'BASE_URL_$titleSlug';
final envs = readEnvs();
String? activeId = readActiveEnvId();
activeId ??= kGlobalEnvironmentId;
final envModel = envs?[activeId];
if (envModel != null) {
final exists = envModel.values.any((v) => v.key == key);
if (!exists) {
final values = [...envModel.values];
values.add(
EnvironmentVariableModel(
key: key,
value: trimmedBase == '/' ? '' : trimmedBase,
enabled: true,
),
);
updateEnv(activeId, values: values);
}
}
return key;
}
String host = 'API';
try {
final u = Uri.parse(baseUrl);
if (u.hasAuthority && u.host.isNotEmpty) host = u.host;
} catch (_) {}
// If host could not be determined (remains 'API'), use title-based slug.
final slug = (host == 'API')
? titleSlug
: host
.replaceAll(RegExp(r'[^A-Za-z0-9]+'), '_')
.replaceAll(RegExp(r'_+'), '_')
.replaceAll(RegExp(r'^_|_$'), '')
.toUpperCase();
final key = 'BASE_URL_$slug';
final envs = readEnvs();
String? activeId = readActiveEnvId();
activeId ??= kGlobalEnvironmentId;
final envModel = envs?[activeId];
if (envModel != null) {
final exists = envModel.values.any((v) => v.key == key);
if (!exists) {
final values = [...envModel.values];
values.add(EnvironmentVariableModel(
key: key,
value: baseUrl,
enabled: true,
));
updateEnv(activeId, values: values);
}
}
return key;
}
/// Build a slug from the first word of an OpenAPI spec title.
/// Example: "Pet Store API" -> "PET"; " My-Orders Service" -> "MY".
/// Falls back to 'API' if no alphanumeric characters are present.
String _slugFromOpenApiTitleFirstWord(String title) {
final trimmed = title.trim();
if (trimmed.isEmpty) return 'API';
// Split on whitespace, take first non-empty token
final firstToken = trimmed.split(RegExp(r'\s+')).firstWhere(
(t) => t.trim().isNotEmpty,
orElse: () => 'API',
);
final cleaned = firstToken
.replaceAll(RegExp(r'[^A-Za-z0-9]+'), '_')
.replaceAll(RegExp(r'_+'), '_')
.replaceAll(RegExp(r'^_|_$'), '')
.toUpperCase();
return cleaned.isEmpty ? 'API' : cleaned;
}
}