Skip to content
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

[Enhancement]: Differentiate between response parsing errors and include the reason in logs #21

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 69 additions & 29 deletions lib/src/translation_delegates/translation_delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:arb_translate/src/flutter_tools/fakes/fake_app_resource_bundle_c
import 'package:arb_translate/src/flutter_tools/gen_l10n_types.dart';
import 'package:arb_translate/src/flutter_tools/localizations_utils.dart';
import 'package:arb_translate/src/translation_delegates/translate_exception.dart';
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';

abstract class TranslationDelegate {
Expand Down Expand Up @@ -111,41 +112,40 @@ abstract class TranslationDelegate {
continue;
}

final result = _tryParseResponse(resources, response);
try {
final result = _tryParseResponse(resources, response);

if (result == null) {
retryCount++;
if (!validateResults(resources, result)) {
retryCount++;

if (retryCount > maxRetryCount) {
throw ResponseParsingException();
}
print(
'Placeholder validation failed for batch $batchName, retrying '
'$retryCount/$maxRetryCount...',
);

print(
'Failed to parse response for $batchName, retrying '
'$retryCount/$maxRetryCount...',
);
if (retryCount > maxRetryCount) {
throw PlaceholderValidationException();
}

continue;
}
continue;
}

if (!validateResults(resources, result)) {
retryCount++;
print('Translated batch $batchName');

print(
'Placeholder validation failed for batch $batchName, retrying '
'$retryCount/$maxRetryCount...',
);
return result;
} on ParsingException catch (e) {
retryCount++;

if (retryCount > maxRetryCount) {
throw PlaceholderValidationException();
throw ResponseParsingException();
}

print('Failed to parse response for $batchName, retrying '
'$retryCount/$maxRetryCount...\n'
'Reason: $e');

continue;
}

print('Translated batch $batchName');

return result;
}
}

Expand All @@ -154,12 +154,12 @@ abstract class TranslationDelegate {
LocaleInfo locale,
);

Map<String, String>? _tryParseResponse(
Map<String, String> _tryParseResponse(
Map<String, Object?> resources,
String? response,
) {
if (response == null) {
return null;
throw EmptyResponseException();
}

final trimmedResponse = response.substring(
Expand All @@ -169,15 +169,20 @@ abstract class TranslationDelegate {

try {
responseJson = json.decode(trimmedResponse);
} catch (e) {
return null;
} on FormatException catch (e) {
throw InvalidJsonException(e);
}

final messageResources =
resources.keys.where((key) => !key.startsWith('@'));

if (messageResources.any((key) => responseJson[key] is! String)) {
return null;
final invalidJsonKey = messageResources
.firstWhereOrNull((key) => responseJson[key] is! String);
if (invalidJsonKey != null) {
throw InvalidResponseValueException(
key: invalidJsonKey,
value: responseJson[invalidJsonKey],
);
}

return {
Expand Down Expand Up @@ -219,3 +224,38 @@ abstract class TranslationDelegate {
return true;
}
}

sealed class ParsingException implements Exception {}

final class EmptyResponseException extends ParsingException {
@override
String toString() {
return 'JSON response was null';
}
}

final class InvalidJsonException implements ParsingException {
final FormatException inner;

const InvalidJsonException(this.inner);

@override
String toString() {
return 'JSON response could not be decoded due to error: $inner';
}
}

final class InvalidResponseValueException<T> implements ParsingException {
final T value;
final String key;

const InvalidResponseValueException({
required this.key,
required this.value,
});

@override
String toString() {
return 'JSON response:$value of key $key could not be casted to the type String';
}
}