Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces Dart quickstarts for Firebase Cloud Functions, featuring both standard and streaming callable function examples. The implementation includes project setup, dependency configurations, and logic for weather forecasting and basic arithmetic. Review feedback highlights opportunities to enhance the robustness of the streaming implementation through better error handling and consistent return types. Additionally, improvements were suggested regarding Dart coding conventions for unused parameters and the adoption of structured logging over standard print statements.
| Future<dynamic> weatherForecastApi(num lat, num lng) async { | ||
| final resp = await http.get( | ||
| Uri.parse('https://api.weather.gov/points/$lat,$lng'), | ||
| ); | ||
|
|
||
| if (resp.statusCode >= 400) { | ||
| return 'error: ${resp.statusCode}'; | ||
| } | ||
|
|
||
| final pointData = jsonDecode(resp.body) as Map<String, dynamic>; | ||
| final forecastUrl = pointData['properties']['forecast'] as String; | ||
| final forecastResp = await http.get(Uri.parse(forecastUrl)); | ||
|
|
||
| if (forecastResp.statusCode >= 400) { | ||
| return 'error: ${forecastResp.statusCode}'; | ||
| } | ||
|
|
||
| // add an artificial wait to emphasize stream-iness | ||
| final randomWait = Random().nextDouble() * 1500; | ||
| await Future<void>.delayed(Duration(milliseconds: randomWait.toInt())); | ||
|
|
||
| return jsonDecode(forecastResp.body); | ||
| } |
There was a problem hiding this comment.
This function returns a String on error and a decoded JSON object (dynamic) on success. This mixed return type can lead to runtime errors on the client if not handled carefully. A better practice is to have a consistent return type and throw an exception on error. The caller can then use a try-catch block for robust error handling.
Future<dynamic> weatherForecastApi(num lat, num lng) async {
final resp = await http.get(
Uri.parse('https://api.weather.gov/points/$lat,$lng'),
);
if (resp.statusCode >= 400) {
throw Exception('Failed to get weather points: ${resp.statusCode}');
}
final pointData = jsonDecode(resp.body) as Map<String, dynamic>;
final forecastUrl = pointData['properties']['forecast'] as String;
final forecastResp = await http.get(Uri.parse(forecastUrl));
if (forecastResp.statusCode >= 400) {
throw Exception('Failed to get weather forecast: ${forecastResp.statusCode}');
}
// add an artificial wait to emphasize stream-iness
final randomWait = Random().nextDouble() * 1500;
await Future<void>.delayed(Duration(milliseconds: randomWait.toInt()));
return jsonDecode(forecastResp.body);
}| final forecast = await weatherForecastApi(latitude, longitude); | ||
| final result = { | ||
| 'latitude': latitude, | ||
| 'longitude': longitude, | ||
| 'forecast': forecast, | ||
| }; | ||
|
|
||
| // clients that support streaming will have each | ||
| // forecast streamed to them as they complete | ||
| if (request.acceptsStreaming) { | ||
| await response.sendChunk(result); | ||
| } | ||
|
|
||
| return result; |
There was a problem hiding this comment.
To handle potential errors from weatherForecastApi gracefully without crashing the entire function for all locations, you should wrap the API call in a try-catch block. This allows you to process other locations even if one fails, and you can stream back a structured error for the failed location. This makes the client-side handling much more robust.
try {
final forecast = await weatherForecastApi(latitude, longitude);
final result = {
'latitude': latitude,
'longitude': longitude,
'forecast': forecast,
};
// clients that support streaming will have each
// forecast streamed to them as they complete
if (request.acceptsStreaming) {
await response.sendChunk(result);
}
return result;
} catch (e, s) {
logger.error(
'Failed to fetch forecast for lat: $latitude, long: $longitude',
error: e,
stackTrace: s,
);
final errorResult = {
'latitude': latitude,
'longitude': longitude,
'error': 'There was an error fetching the forecast for this location.',
};
if (request.acceptsStreaming) {
await response.sendChunk(errorResult);
}
return errorResult;
}| // [START allAdd] | ||
| // [START addFunctionTrigger] | ||
| // Adds two numbers to each other. | ||
| firebase.https.onCall(name: 'addNumbers', (request, response) async { |
There was a problem hiding this comment.
The response parameter is not used in this function. According to Dart conventions, it's good practice to rename unused parameters to _ or prefix them with _ (e.g., _response) to signal that they are intentionally unused.
| firebase.https.onCall(name: 'addNumbers', (request, response) async { | |
| firebase.https.onCall(name: 'addNumbers', (request, _) async { |
| print( | ||
| 'User details: uid=$uid, name=$name, picture=$picture, email=$email', | ||
| ); |
There was a problem hiding this comment.
For logging in Cloud Functions, it's better to use the structured logger provided by firebase_functions instead of print. The logger automatically adds metadata to logs, which makes them easier to search and filter in Cloud Logging.
| print( | |
| 'User details: uid=$uid, name=$name, picture=$picture, email=$email', | |
| ); | |
| logger.info( | |
| 'User details: uid=$uid, name=$name, picture=$picture, email=$email', | |
| ); |
No description provided.