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

chore(amplify_api): add httpStatusCode property to ApiException when available from REST response #590

Merged
merged 7 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,16 @@ class FlutterRestApi {
"""

// if code is not 200 then throw an exception
/*
result.code.toString = "Code{" +
"statusCode=" + statusCode +
'}';
*/
if (!result.code.isSuccessful) {
handler.post {
ExceptionUtil.postExceptionToFlutterChannel(flutterResult, "ApiException",
ExceptionUtil.createSerializedError(
ApiException(
"The HTTP response status code is [" + result.code.toString().substring(16, 19) + "].",
recoverySuggestion)
)
var httpStatusCode = result.code?.hashCode()?.toString()
var serializedError = ExceptionUtil.createSerializedError(
ApiException(
"The HTTP response status code is [$httpStatusCode].",
recoverySuggestion)
)
var serializedErrorWithStatusCode = mapOf("httpStatusCode" to httpStatusCode) + serializedError
ExceptionUtil.postExceptionToFlutterChannel(flutterResult, "ApiException", serializedErrorWithStatusCode)
}
return
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@ class AmplifyApiRestTest {
For more information on HTTP status codes, take a look at
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
""",
"message" to "The HTTP response status code is [400]."
"message" to "The HTTP response status code is [400].",
"httpStatusCode" to "400"
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ class RestApiUnitTests: XCTestCase {

XCTAssertEqual(referenceError.errorDescription, errorMap["message"])
XCTAssertEqual(referenceError.recoverySuggestion, errorMap["recoverySuggestion"])
XCTAssertEqual("400", errorMap["httpStatusCode"])
}
)
}
Expand Down
10 changes: 9 additions & 1 deletion packages/amplify_api/ios/Classes/FlutterApiErrorHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ class FlutterApiErrorHandler {
}

static func createSerializedError(error: APIError) -> Dictionary<String, String> {
let httpStatusCode: String?
switch error {
case .httpStatusError(let statusCode, _):
httpStatusCode = String(statusCode)
default:
httpStatusCode = nil
}
return ErrorUtil.createSerializedError(message: error.errorDescription,
recoverySuggestion: error.recoverySuggestion,
underlyingError: error.underlyingError?.localizedDescription)
underlyingError: error.underlyingError?.localizedDescription,
httpStatusCode: httpStatusCode)
}

}
2 changes: 1 addition & 1 deletion packages/amplify_api/ios/Classes/FlutterApiResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class FlutterApiResponse {
print("An unknown error occured: \(errorDescription)")

ErrorUtil.postErrorToFlutterChannel(result: flutterResult, errorCode: "ApiException",
details: ErrorUtil.createSerializedError(message: errorDescription, recoverySuggestion: recoverySuggestion, underlyingError: nil))
details: ErrorUtil.createSerializedError(message: errorDescription, recoverySuggestion: recoverySuggestion, underlyingError: nil, httpStatusCode: nil))
}
}

Expand Down
72 changes: 72 additions & 0 deletions packages/amplify_api/test/amplify_rest_api_methods_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,78 @@ void main() {
}
});

test('GET exception adds the httpStatusCode to exception if available',
() async {
apiChannel.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == "get") {
throw PlatformException(code: 'ApiException', details: {
'message': 'AMPLIFY_API_MUTATE_FAILED',
'recoverySuggestion': 'some insightful suggestion',
'underlyingException': 'Act of God',
'httpStatusCode': '500'
});
}
});

try {
RestOperation restOperation = api.get(
restOptions: RestOptions(
path: "/items",
));
await restOperation.response;
} on ApiException catch (e) {
expect(e.httpStatusCode, 500);
}
});

test('GET exception does not add httpStatusCode if not a valid status code',
() async {
apiChannel.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == "get") {
throw PlatformException(code: 'ApiException', details: {
'message': 'AMPLIFY_API_MUTATE_FAILED',
'recoverySuggestion': 'some insightful suggestion',
'underlyingException': 'Act of God',
'httpStatusCode': '999'
});
}
});

try {
RestOperation restOperation = api.get(
restOptions: RestOptions(
path: "/items",
));
await restOperation.response;
} on ApiException catch (e) {
expect(e.httpStatusCode, null);
}
});

test(
'GET exception does not add httpStatusCode if not available in serialized error',
() async {
apiChannel.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == "get") {
throw PlatformException(code: 'ApiException', details: {
'message': 'AMPLIFY_API_MUTATE_FAILED',
'recoverySuggestion': 'some insightful suggestion',
'underlyingException': 'Act of God',
});
}
});

try {
RestOperation restOperation = api.get(
restOptions: RestOptions(
path: "/items",
));
await restOperation.response;
} on ApiException catch (e) {
expect(e.httpStatusCode, null);
}
});

test('CANCEL success does not throw error', () async {
// Need to reply with PLACEHOLDER to avoid null issues in _formatRestResponse
// In actual production code, the methodChannel doesn't respond to the future response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,35 @@ import 'package:amplify_core/types/exception/AmplifyException.dart';

/// Exception thrown from Api Category
class ApiException extends AmplifyException {
/// HTTP status of response, only available if error
final int httpStatusCode;

/// Named constructor
const ApiException(String message,
{String recoverySuggestion, String underlyingException})
: super(message,
ApiException(String message,
{String recoverySuggestion, String underlyingException, int statusCode})
: httpStatusCode = statusCode,
super(message,
recoverySuggestion: recoverySuggestion,
underlyingException: underlyingException);

/// Constructor for down casting an AmplifyException to this exception
ApiException._private(AmplifyException exception)
: super(exception.message,
ApiException._private(
AmplifyException exception, int httpStatusCodeFromException)
: httpStatusCode = httpStatusCodeFromException,
super(exception.message,
recoverySuggestion: exception.recoverySuggestion,
underlyingException: exception.underlyingException);

/// Instantiates and return a new `ApiException` from the
/// serialized exception data
static ApiException fromMap(Map<String, String> serializedException) {
return ApiException._private(AmplifyException.fromMap(serializedException));
var statusCode =
int.tryParse(serializedException["httpStatusCode"] ?? "") ?? null;
// Ensure a valid HTTP status code for an error.
if (statusCode != null && (statusCode < 300 || statusCode > 511)) {
statusCode = null;
}
return ApiException._private(
AmplifyException.fromMap(serializedException), statusCode);
}
}
4 changes: 3 additions & 1 deletion packages/amplify_core/ios/Classes/Support/ErrorUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ public class ErrorUtil {

public static func createSerializedError(message: String,
recoverySuggestion: String?,
underlyingError: String?) -> Dictionary<String, String> {
underlyingError: String?,
httpStatusCode: String?) -> Dictionary<String, String> {
var serializedException: Dictionary<String, String> = [:]
serializedException["message"] = message
serializedException["recoverySuggestion"] = recoverySuggestion
serializedException["underlyingException"] = underlyingError
serializedException["httpStatusCode"] = httpStatusCode
return serializedException
}
}