Skip to content

Commit fe7c739

Browse files
authored
feat: add http onCall triggers (#8)
1 parent 5272cb5 commit fe7c739

File tree

9 files changed

+2061
-90
lines changed

9 files changed

+2061
-90
lines changed

example/basic/lib/main.dart

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,75 @@ final isProduction = defineBoolean(
3434

3535
void main(List<String> args) {
3636
fireUp(args, (firebase) {
37+
// ==========================================================================
38+
// HTTPS Callable Functions (onCall / onCallWithData)
39+
// ==========================================================================
40+
41+
// Basic callable function - untyped data
42+
firebase.https.onCall(
43+
name: 'greet',
44+
(request, response) async {
45+
final data = request.data as Map<String, dynamic>?;
46+
final name = data?['name'] ?? 'World';
47+
return CallableResult({'message': 'Hello, $name!'});
48+
},
49+
);
50+
51+
// Callable function with typed data using fromJson
52+
firebase.https.onCallWithData<GreetRequest, GreetResponse>(
53+
name: 'greetTyped',
54+
fromJson: GreetRequest.fromJson,
55+
(request, response) async {
56+
return GreetResponse(message: 'Hello, ${request.data.name}!');
57+
},
58+
);
59+
60+
// Callable function demonstrating error handling
61+
firebase.https.onCall(
62+
name: 'divide',
63+
(request, response) async {
64+
final data = request.data as Map<String, dynamic>?;
65+
final a = (data?['a'] as num?)?.toDouble();
66+
final b = (data?['b'] as num?)?.toDouble();
67+
68+
if (a == null || b == null) {
69+
throw InvalidArgumentError('Both "a" and "b" are required');
70+
}
71+
72+
if (b == 0) {
73+
throw FailedPreconditionError('Cannot divide by zero');
74+
}
75+
76+
return CallableResult({'result': a / b});
77+
},
78+
);
79+
80+
// Callable function with streaming support
81+
firebase.https.onCall(
82+
name: 'countdown',
83+
options: const CallableOptions(
84+
heartBeatIntervalSeconds: HeartBeatIntervalSeconds(5),
85+
),
86+
(request, response) async {
87+
final data = request.data as Map<String, dynamic>?;
88+
final start = (data?['start'] as num?)?.toInt() ?? 10;
89+
90+
// Stream countdown if client supports it
91+
if (request.acceptsStreaming) {
92+
for (var i = start; i >= 0; i--) {
93+
await response.sendChunk({'count': i});
94+
await Future<void>.delayed(const Duration(milliseconds: 100));
95+
}
96+
}
97+
98+
return CallableResult({'message': 'Countdown complete!'});
99+
},
100+
);
101+
102+
// ==========================================================================
103+
// HTTPS onRequest Functions
104+
// ==========================================================================
105+
37106
// HTTPS onRequest example - using parameterized configuration
38107
firebase.https.onRequest(
39108
name: 'helloWorld',
@@ -251,3 +320,29 @@ void main(List<String> args) {
251320
print('Functions registered successfully!');
252321
});
253322
}
323+
324+
// =============================================================================
325+
// Data classes for typed callable functions
326+
// =============================================================================
327+
328+
/// Request data for the greetTyped callable function.
329+
class GreetRequest {
330+
GreetRequest({required this.name});
331+
332+
factory GreetRequest.fromJson(Map<String, dynamic> json) {
333+
return GreetRequest(name: json['name'] as String? ?? 'World');
334+
}
335+
336+
final String name;
337+
338+
Map<String, dynamic> toJson() => {'name': name};
339+
}
340+
341+
/// Response data for the greetTyped callable function.
342+
class GreetResponse {
343+
GreetResponse({required this.message});
344+
345+
final String message;
346+
347+
Map<String, dynamic> toJson() => {'message': message};
348+
}

example/nodejs_reference/index.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* generates compatible functions.yaml output.
55
*/
66

7-
const { onRequest } = require("firebase-functions/v2/https");
7+
const { onRequest, onCall, HttpsError } = require("firebase-functions/v2/https");
88
const { onMessagePublished } = require("firebase-functions/v2/pubsub");
99
const { onDocumentCreated, onDocumentUpdated, onDocumentDeleted, onDocumentWritten } = require("firebase-functions/v2/firestore");
1010
const { onValueCreated, onValueUpdated, onValueDeleted, onValueWritten } = require("firebase-functions/v2/database");
@@ -33,6 +33,53 @@ const isProduction = defineBoolean("IS_PRODUCTION", {
3333
description: "Whether this is a production deployment",
3434
});
3535

36+
// =============================================================================
37+
// HTTPS Callable Functions (onCall)
38+
// =============================================================================
39+
40+
// Basic callable function - untyped data
41+
exports.greet = onCall((request) => {
42+
const name = request.data?.name ?? "World";
43+
return { message: `Hello, ${name}!` };
44+
});
45+
46+
// Callable function with typed data (same manifest structure as untyped)
47+
exports.greetTyped = onCall((request) => {
48+
const name = request.data?.name ?? "World";
49+
return { message: `Hello, ${name}!` };
50+
});
51+
52+
// Callable function demonstrating error handling
53+
exports.divide = onCall((request) => {
54+
const a = request.data?.a;
55+
const b = request.data?.b;
56+
57+
if (a === undefined || b === undefined) {
58+
throw new HttpsError("invalid-argument", 'Both "a" and "b" are required');
59+
}
60+
61+
if (b === 0) {
62+
throw new HttpsError("failed-precondition", "Cannot divide by zero");
63+
}
64+
65+
return { result: a / b };
66+
});
67+
68+
// Callable function with streaming support
69+
exports.countdown = onCall(
70+
{
71+
// heartbeatSeconds is a runtime option, not in manifest
72+
},
73+
(request) => {
74+
// Streaming is handled at runtime, not in manifest
75+
return { message: "Countdown complete!" };
76+
}
77+
);
78+
79+
// =============================================================================
80+
// HTTPS onRequest Functions
81+
// =============================================================================
82+
3683
// HTTPS onRequest example - using parameterized configuration
3784
exports.helloWorld = onRequest(
3885
{

0 commit comments

Comments
 (0)