Skip to content

make ErrorResponse#errorType efficient and impossible to mis-represent #50

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

Closed
Closed
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
59 changes: 40 additions & 19 deletions lambda-runtime-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,30 @@ use hyper;
use serde_derive::Serialize;
use serde_json;

/// Error type description for the `ErrorResponse` event. This type should be returned
/// for errors that were handled by the function code or framework.
pub const ERROR_TYPE_HANDLED: &str = "Handled";
/// Error type description for the `ErrorResponse` event. This type is used for unhandled,
/// unexpcted errors.
pub const ERROR_TYPE_UNHANDLED: &str = "Unhandled";
/// Error type description for the `ErrorResponse` event.
#[derive(Serialize)]
pub enum ErrorType {
/// This type should be returned
/// for errors that were handled by the function code or framework.
Handled,
/// This type is used for unhandled,
/// unexpcted errors.
Unhandled,
}

/// This object is used to generate requests to the Lambda Runtime APIs.
/// It is used for both the error response APIs and fail init calls.
/// custom error types should implement the `RuntimeError` trait and return
/// this object to be compatible with the APIs.
#[derive(Serialize)]
#[serde(rename_all="camelCase")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this removes some potential for error prone manual string formatted by using a feature built into serde that will do the formatting for you resulting in less code and less error-proneness.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, thanks.

pub struct ErrorResponse {
/// The error message generated by the application.
#[serde(rename = "errorMessage")]
pub error_message: String,
/// The error type for Lambda. This can be `Handled` or `Unhandled`.
/// Developers can use the `ERROR_TYPE_HANDLED` and `ERROR_TYPE_UNHANDLED`
/// constants to populate this field.
#[serde(rename = "errorType")]
pub error_type: String,
/// The error type for Lambda.
pub error_type: ErrorType,
/// The stack trace for the exception as vector of strings. In the framework,
/// this value is automatically populated using the `backtrace` crate.
#[serde(rename = "stackTrace")]
pub stack_trace: Option<Vec<String>>,
}

Expand All @@ -45,10 +45,10 @@ impl ErrorResponse {
///
/// # Return
/// A populated `RuntimeError` object that can be used with the Lambda Runtime API.
pub fn handled(message: String) -> ErrorResponse {
pub fn handled<M>(message: M) -> ErrorResponse where M: Into<String> {
ErrorResponse {
error_message: message,
error_type: String::from(ERROR_TYPE_HANDLED),
error_message: message.into(),
error_type: ErrorType::Handled,
stack_trace: Option::default(),
}
}
Expand All @@ -61,10 +61,10 @@ impl ErrorResponse {
///
/// # Return
/// A populated `RuntimeError` object that can be used with the Lambda Runtime API.
pub fn unhandled(message: String) -> ErrorResponse {
pub fn unhandled<M>(message: M) -> ErrorResponse where M: Into<String> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

happen to back out of this but in general this is fmt is typically more ergonomic in rust as it now accepts anything that implements Into<String> like slices and the like

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

happen to back out of this but in general this is fmt is typically more ergonomic in rust as it now accepts anything that implements Into like slices and the like.

Nice catch, agreed. we were a little pressed before release, so it's nice that you're catching all these little gotchas that we missed first time around...

ErrorResponse {
error_message: message,
error_type: String::from(ERROR_TYPE_UNHANDLED),
error_message: message.into(),
error_type: ErrorType::Unhandled,
stack_trace: Option::default(),
}
}
Expand Down Expand Up @@ -182,3 +182,24 @@ impl RuntimeApiError for ApiError {
err
}
}

#[cfg(test)]
mod tests {
use super::ErrorResponse;

#[test]
fn handled_error_response_serializes() {
assert_eq!(
serde_json::to_string(&ErrorResponse::handled("💀")).expect("failed to serialize ErrorResponse"),
r#"{"errorMessage":"💀","errorType":"Handled","stackTrace":null}"#
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated, but note the json null ( serde default for Option::None ). we may want to catch this in a follow up if that's not intentional

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure, I've reached out to the lambda runtime team about this.

)
}

#[test]
fn unhandled_error_response_serializes() {
assert_eq!(
serde_json::to_string(&ErrorResponse::unhandled("💀")).expect("failed to serialize ErrorResponse"),
r#"{"errorMessage":"💀","errorType":"Unhandled","stackTrace":null}"#
)
}
}
4 changes: 2 additions & 2 deletions lambda-runtime/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl error::RuntimeApiError for RuntimeError {
let backtrace = format!("{:?}", self.stack_trace);
error::ErrorResponse {
error_message: String::from(self.description()),
error_type: String::from(error::ERROR_TYPE_HANDLED),
error_type: error::ErrorType::Handled,
stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::<Vec<String>>()),
}
}
Expand Down Expand Up @@ -173,7 +173,7 @@ impl error::RuntimeApiError for HandlerError {
let backtrace = format!("{:?}", self.backtrace);
error::ErrorResponse {
error_message: String::from(self.description()),
error_type: String::from(error::ERROR_TYPE_HANDLED),
error_type: error::ErrorType::Handled,
stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::<Vec<String>>()),
}
}
Expand Down