Skip to content
Draft
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
2 changes: 1 addition & 1 deletion example/auth/bin/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void main(List<String> args) async {
// Example: Block users with certain email domains
final email = user?.email;
if (email != null && email.endsWith('@blocked.com')) {
throw PermissionDeniedError('Email domain not allowed');
throw HttpResponseException(403, 'Email domain not allowed');
}

// Example: Set custom claims based on email domain
Expand Down
4 changes: 2 additions & 2 deletions example/https/bin/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ void main(List<String> args) async {
final b = (data?['b'] as num?)?.toDouble();

if (a == null || b == null) {
throw InvalidArgumentError('Both "a" and "b" are required');
throw HttpResponseException(400, 'Both "a" and "b" are required');
}

if (b == 0) {
throw FailedPreconditionError('Cannot divide by zero');
throw HttpResponseException(400, 'Cannot divide by zero');
}

return CallableResult({'result': a / b});
Expand Down
4 changes: 2 additions & 2 deletions lib/firebase_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ library;
import 'params.dart' as params;

// Package re-exports
export 'package:google_cloud/google_cloud.dart'
show CloudLogger, LogSeverity, currentLogger;
export 'package:google_cloud_firestore/google_cloud_firestore.dart'
show DocumentData, DocumentSnapshot, QueryDocumentSnapshot;
export 'package:shelf/shelf.dart' show Request, Response;
Expand All @@ -108,8 +110,6 @@ export 'src/firestore/firestore.dart';
export 'src/https/https.dart';
// Experimental: Identity triggers (not yet supported in production or emulator)
export 'src/identity/identity.dart';
// Logger
export 'src/logger/logger.dart' show LogEntry, LogSeverity, Logger, logger;
// Experimental: Pub/Sub triggers (not yet supported in production or emulator)
export 'src/pubsub/pubsub.dart';
// Experimental: Remote Config triggers (not yet supported in production or emulator)
Expand Down
57 changes: 0 additions & 57 deletions lib/logger.dart

This file was deleted.

3 changes: 0 additions & 3 deletions lib/src/alerts/alerts_namespace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import 'package:meta/meta.dart';
import 'package:shelf/shelf.dart';

import '../common/cloud_event.dart';
import '../common/utilities.dart';
import '../firebase.dart';
import 'alert_event.dart';
import 'alert_type.dart';
Expand Down Expand Up @@ -102,8 +101,6 @@ class AlertsNamespace extends FunctionsNamespace {
return Response.ok('');
} on FormatException catch (e) {
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
} catch (e, stackTrace) {
return logEventHandlerError(e, stackTrace);
}
});
}
Expand Down
3 changes: 0 additions & 3 deletions lib/src/alerts/app_distribution_namespace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import 'package:meta/meta.dart';
import 'package:shelf/shelf.dart';

import '../common/cloud_event.dart';
import '../common/utilities.dart';
import '../firebase.dart';
import 'alert_event.dart';
import 'alert_type.dart';
Expand Down Expand Up @@ -84,8 +83,6 @@ class AppDistributionNamespace {
return Response.ok('');
} on FormatException catch (e) {
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
} catch (e, stackTrace) {
return logEventHandlerError(e, stackTrace);
}
});
}
Expand Down
3 changes: 0 additions & 3 deletions lib/src/alerts/billing_namespace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import 'package:meta/meta.dart';
import 'package:shelf/shelf.dart';

import '../common/cloud_event.dart';
import '../common/utilities.dart';
import '../firebase.dart';
import 'alert_event.dart';
import 'alert_type.dart';
Expand Down Expand Up @@ -85,8 +84,6 @@ class BillingNamespace {
return Response.ok('');
} on FormatException catch (e) {
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
} catch (e, stackTrace) {
return logEventHandlerError(e, stackTrace);
}
});
}
Expand Down
3 changes: 0 additions & 3 deletions lib/src/alerts/crashlytics_namespace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import 'package:meta/meta.dart';
import 'package:shelf/shelf.dart';

import '../common/cloud_event.dart';
import '../common/utilities.dart';
import '../firebase.dart';
import 'alert_event.dart';
import 'alert_type.dart';
Expand Down Expand Up @@ -141,8 +140,6 @@ class CrashlyticsNamespace {
return Response.ok('');
} on FormatException catch (e) {
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
} catch (e, stackTrace) {
return logEventHandlerError(e, stackTrace);
}
});
}
Expand Down
3 changes: 0 additions & 3 deletions lib/src/alerts/performance_namespace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import 'package:meta/meta.dart';
import 'package:shelf/shelf.dart';

import '../common/cloud_event.dart';
import '../common/utilities.dart';
import '../firebase.dart';
import 'alert_event.dart';
import 'alert_type.dart';
Expand Down Expand Up @@ -70,8 +69,6 @@ class PerformanceNamespace {
return Response.ok('');
} on FormatException catch (e) {
return Response(400, body: 'Invalid CloudEvent: ${e.message}');
} catch (e, stackTrace) {
return logEventHandlerError(e, stackTrace);
}
});
}
Expand Down
12 changes: 3 additions & 9 deletions lib/src/common/environment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import 'dart:convert';
import 'dart:io';

import 'package:google_cloud/constants.dart';
import 'package:meta/meta.dart';

/// Provides unified access to environment variables, emulator checks, and
Expand Down Expand Up @@ -83,17 +84,12 @@ class FirebaseEnv {
);
}

/// The port to listen on.
///
/// Uses the `PORT` environment variable, defaulting to 8080.
int get port => int.tryParse(environment['PORT'] ?? '8080') ?? 8080;

/// The name of the Cloud Run service.
///
/// Uses the `K_SERVICE` environment variable.
///
/// See https://cloud.google.com/run/docs/container-contract#env-vars
String? get kService => environment['K_SERVICE'];
String? get kService => environment[serviceEnvironmentVariable];

/// The name of the target function.
///
Expand All @@ -114,9 +110,7 @@ class FirebaseEnv {
/// Common project ID environment variables checked in order.
const _projectIdEnvKeyOptions = [
'FIREBASE_PROJECT',
'GCLOUD_PROJECT',
'GOOGLE_CLOUD_PROJECT',
'GCP_PROJECT',
...projectIdEnvironmentVariableOptions,
];

/// Common emulator host keys used to detect emulator environment.
Expand Down
4 changes: 3 additions & 1 deletion lib/src/common/on_init.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ library;

import 'dart:async';

import 'package:google_cloud/google_cloud.dart';

/// Callback registered via [onInit].
FutureOr<void> Function()? _initCallback;

Expand Down Expand Up @@ -73,7 +75,7 @@ bool _didInit = false;
/// - [defineJsonSecret] for JSON-encoded secrets
void onInit(FutureOr<void> Function() callback) {
if (_initCallback != null) {
print(
currentLogger.warning(
'Warning: Setting onInit callback more than once. '
'Only the most recent callback will be called.',
);
Expand Down
44 changes: 21 additions & 23 deletions lib/src/common/params.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import 'dart:convert';
import 'dart:io';

import 'package:google_cloud/google_cloud.dart';

import 'expression.dart';

// ============================================================================
Expand Down Expand Up @@ -354,13 +356,11 @@ abstract class Param<T extends Object> extends Expression<T> {
@override
T value() {
if (Platform.environment['FUNCTIONS_CONTROL_API'] == 'true') {
print(
'Warning: ${toString()}.value() invoked during function deployment, '
'instead of during runtime.\n'
'This is usually a mistake. In configs, use Params directly without '
'calling .value().\n'
'Example: HttpsOptions(minInstances: minInstancesParam) '
'not HttpsOptions(minInstances: Option(minInstancesParam.value()))',
currentLogger.warning(
'''
${toString()}.value() invoked during function deployment, instead of during runtime.
This is usually a mistake. In configs, use Params directly without calling .value().
Example: HttpsOptions(minInstances: minInstancesParam) not HttpsOptions(minInstances: Option(minInstancesParam.value()))''',
);
}
return runtimeValue();
Expand Down Expand Up @@ -415,11 +415,10 @@ class SecretParam extends Param<String> {
String runtimeValue() {
final val = Platform.environment[name];
if (val == null) {
print(
'Warning: No value found for secret parameter "$name". '
'A function can only access a secret if you include the secret '
'in the function\'s secrets array.',
);
currentLogger.warning('''
No value found for secret parameter "$name".
A function can only access a secret if you include the secret in the function's secrets array.
''');
return '';
}
return val;
Expand Down Expand Up @@ -699,12 +698,12 @@ class ListParam extends Param<List<String>> {
if (parsed is List && parsed.every((v) => v is String)) {
return List<String>.from(parsed);
}
} on FormatException {
} on FormatException catch (e, stack) {
// Invalid JSON, return default
print(
'Warning: Failed to parse list parameter "$name" as JSON array. '
'Expected format: \'["value1", "value2"]\'. Returning default value.',
);
currentLogger.warning('''
Failed to parse list parameter "$name" as JSON array.
Expected format: `["value1", "value2"]`. Returning default value.
''', stackTrace: stack);
}

return options?.defaultValue ?? [];
Expand Down Expand Up @@ -753,12 +752,11 @@ class EnumListParam<T extends Enum> extends Param<List<T>> {
}
return result;
}
} on FormatException catch (e) {
print(
'Warning: Failed to parse enum list parameter "$name". '
'Expected format: \'["value1", "value2"]\'. Error: $e. '
'Returning default value.',
);
} on FormatException catch (e, stack) {
currentLogger.warning('''
Failed to parse enum list parameter "$name" as JSON array.
Expected format: `["value1", "value2"]`. Error: $e. Returning default value.
''', stackTrace: stack);
}

return options?.defaultValue ?? [];
Expand Down
41 changes: 1 addition & 40 deletions lib/src/common/utilities.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@

import 'dart:convert';

import 'package:shelf/shelf.dart' show Request, Response;
import 'package:stack_trace/stack_trace.dart' show Trace;

import '../https/error.dart';
import '../logger/logger.dart';
import 'package:shelf/shelf.dart' show Request;

Future<Map<String, dynamic>> readAsJsonMap(Request request) async {
final decoded = await _converter.bind(request.read()).first;
Expand All @@ -29,38 +25,3 @@ Future<Map<String, dynamic>> readAsJsonMap(Request request) async {
}

final _converter = const Utf8Decoder().fuse(const JsonDecoder());

extension HttpErrorExtension on HttpsError {
Response toShelfResponse() => Response(
httpStatusCode,
headers: {'Content-Type': 'application/json'},
body: jsonEncode(toErrorResponse()),
);
}

/// Logs an unexpected error with its stack trace and returns an [InternalError].
///
/// Use in HTTPS and callable function handlers where the response must be
/// a structured JSON error. The actual error details are only logged
/// server-side and never exposed to the client.
InternalError logInternalError(Object error, StackTrace stackTrace) {
_logError(error, stackTrace);
return InternalError();
}

/// Logs an unexpected error with its stack trace and returns a generic 500
/// response.
///
/// Use in event-triggered function handlers (Firestore, PubSub, Storage, etc.)
/// where the caller is the Cloud Functions infrastructure rather than an
/// end-user client.
Response logEventHandlerError(Object error, StackTrace stackTrace) {
_logError(error, stackTrace);
return Response.internalServerError();
}

/// Formats and logs an error with a terse, readable stack trace.
void _logError(Object error, StackTrace stackTrace) {
final terse = Trace.from(stackTrace).terse;
logger.error('$error\n$terse');
}
Loading