fix: handle security fields with empty arrays

This commit is contained in:
Udhay-Adithya
2025-10-02 03:49:26 +05:30
parent 314617e2e7
commit 465234477d
2 changed files with 120 additions and 2 deletions

View File

@@ -132,15 +132,53 @@ class OpenApiImportService {
/// Try to parse a JSON or YAML OpenAPI spec string. /// Try to parse a JSON or YAML OpenAPI spec string.
/// Returns null if parsing fails. /// Returns null if parsing fails.
///
/// NOTE: There's a known issue with the openapi_spec package where
/// security fields containing empty arrays (e.g., "security": [[]])
/// cause parsing failures. This method includes a workaround.
static OpenApi? tryParseSpec(String source) { static OpenApi? tryParseSpec(String source) {
try { try {
// Let the library infer JSON/YAML
return OpenApi.fromString(source: source, format: null); return OpenApi.fromString(source: source, format: null);
} catch (_) { } catch (e) {
// Try workaround for security field parsing issues
try {
final processedSource = _removeProblematicSecurityField(source);
if (processedSource != source) {
return OpenApi.fromString(source: processedSource, format: null);
}
} catch (_) {
// Workaround failed, fall through to return null
}
return null; return null;
} }
} }
/// Removes problematic security fields that cause parsing issues.
/// TODO: Remove this workaround once openapi_spec package fixes
/// the issue with security fields containing empty arrays.
static String _removeProblematicSecurityField(String source) {
try {
final spec = jsonDecode(source) as Map<String, dynamic>;
if (spec.containsKey('security')) {
final security = spec['security'];
if (security is List && _hasEmptySecurityArrays(security)) {
spec.remove('security');
return jsonEncode(spec);
}
}
return source;
} catch (e) {
throw FormatException('Failed to preprocess OpenAPI spec: $e');
}
}
/// Checks if security list contains empty arrays that cause parsing issues.
static bool _hasEmptySecurityArrays(List<dynamic> security) {
return security.any((item) => item is List && item.isEmpty);
}
/// Build a single request payload from a path + method operation. /// Build a single request payload from a path + method operation.
/// The payload mirrors CurlImportService payload shape for reuse. /// The payload mirrors CurlImportService payload shape for reuse.
static Map<String, dynamic> _payloadForOperation({ static Map<String, dynamic> _payloadForOperation({

View File

@@ -31,6 +31,86 @@ void main() {
expect(summary, contains('POST')); expect(summary, contains('POST'));
}); });
test('tryParseSpec handles problematic security field with empty arrays',
() {
const specWithEmptySecurityArray = '''
{
"openapi": "3.0.0",
"info": {
"title": "Cat Fact API",
"version": "1.0.0"
},
"paths": {
"/fact": {
"get": {
"responses": {
"200": {
"description": "Success"
}
}
}
}
},
"security": [[]]
}''';
final result =
OpenApiImportService.tryParseSpec(specWithEmptySecurityArray);
expect(result, isNotNull);
expect(result!.info.title, equals('Cat Fact API'));
expect(result.info.version, equals('1.0.0'));
expect(result.paths, isNotNull);
expect(result.paths!.keys, contains('/fact'));
});
test('tryParseSpec handles valid security field with actual requirements',
() {
const specWithRealSecurity = '''
{
"openapi": "3.0.0",
"info": {
"title": "Secured API",
"version": "1.0.0"
},
"paths": {
"/secured": {
"get": {
"responses": {
"200": {
"description": "Success"
}
}
}
}
},
"security": [
{
"api_key": []
}
]
}''';
final result = OpenApiImportService.tryParseSpec(specWithRealSecurity);
expect(result, isNotNull);
expect(result!.info.title, equals('Secured API'));
});
test('tryParseSpec returns null for invalid JSON', () {
const invalidSpec = 'not valid json';
final result = OpenApiImportService.tryParseSpec(invalidSpec);
expect(result, isNull);
});
test('tryParseSpec returns null for non-OpenAPI JSON', () {
const nonOpenApiSpec = '''
{
"notOpenApi": true,
"someField": "value"
}''';
final result = OpenApiImportService.tryParseSpec(nonOpenApiSpec);
expect(result, isNull);
});
test('extractSpecMeta includes endpoints & baseUrl', () { test('extractSpecMeta includes endpoints & baseUrl', () {
final spec = OpenApiImportService.tryParseSpec(_specJson)!; final spec = OpenApiImportService.tryParseSpec(_specJson)!;
final meta = OpenApiImportService.extractSpecMeta(spec); final meta = OpenApiImportService.extractSpecMeta(spec);