Skip to content

Commit

Permalink
wip: scheduler support for new function interop
Browse files Browse the repository at this point in the history
  • Loading branch information
alextekartik committed Jun 14, 2024
1 parent e0898d3 commit 3bf8774
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 165 deletions.
2 changes: 1 addition & 1 deletion functions_node/lib/src/node/express_http_request_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'dart:typed_data';

import 'package:tekartik_firebase_functions/firebase_functions.dart';
import 'package:tekartik_firebase_functions_node/src/import_common.dart';
import 'package:tekartik_firebase_functions_node/src/node/firebase_functions_node_js_interop.dart'
import 'package:tekartik_firebase_functions_node/src/node/firebase_functions_https_node_js_interop.dart'
as node;
import 'package:tekartik_http/http.dart' as http;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright (c) 2018, Anatoly Pulyaevskiy. All rights reserved. Use of this source code
// is governed by a BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:js_interop' as js;

import 'package:tekartik_firebase_functions_node/src/import_common.dart';
import 'firebase_functions_node_js_interop.dart';

extension type JSHttpsFunctions._(js.JSObject _) implements js.JSObject {}

extension JSHttpsFunctionsExt on JSHttpsFunctions {
// Handles HTTPS requests.
//
// Signature:
// export declare function onRequest(opts: HttpsOptions, handler: (request: Request, response: express.Response) => void | Promise<void>): HttpsFunction;

@js.JS('onRequest')
external JSHttpsFunction _onRequest(
JSHttpsOptions options, _JSHttpsHandler handler);

@js.JS('onRequest')
external JSHttpsFunction _onRequestNoOptions(_JSHttpsHandler handler);

JSHttpsFunction onRequest(
{JSHttpsOptions? options, required HttpsHandler handler}) {
js.JSAny? jsHandler(JSHttpsRequest request, JSHttpsResponse response) {
var result = handler(request, response);
if (result is Future) {
return result.toJS;
} else {
return null;
}
}

if (options == null) {
//devPrint('Setting handler no options');
return _onRequestNoOptions(jsHandler.toJS);
} else {
// devPrint('Setting handler');
return _onRequest(options, jsHandler.toJS);
}
}
}

// An express request with the wire format representation of the request body.
//
extension type JSHttpsRequest._(js.JSObject _) implements js.JSObject {}

extension JSHttpsRequestExt on JSHttpsRequest {
/// From node IncomingMessage
/// Request URL string. This contains only the URL that is present in the actual HTTP request.
/// For example: '/status?name=ryan'
external String get url;

/// Firebase function extension
///
/// Buffer The wire format representation of the request body.
/// Buffer is not only and extends Uint8Array
external js.JSUint8Array? get rawBody;

/// The request method as a string. Read only. Examples: 'GET', 'DELETE'.
external String get method;

/// The request/response headers object.
///
/// Key-value pairs of header names and values. Header names are lower-cased.
///
/// Prints something like:
///
/// { 'user-agent': 'curl/7.22.0',
/// host: '127.0.0.1:8000',
/// accept: '*/*' }
/// console.log(request.headers); COPY
/// Duplicates in raw headers are handled in the following ways, depending on the header name:
///
/// Duplicates of age, authorization, content-length, content-type, etag, expires, from, host, if-modified-since, if-unmodified-since, last-modified, location, max-forwards, proxy-authorization, referer, retry-after, server, or user-agent are discarded. To allow duplicate values of the headers listed above to be joined, use the option joinDuplicateHeaders in http.request() and http.createServer(). See RFC 9110 Section 5.3 for more information.
/// set-cookie is always an array. Duplicates are added to the array.
/// For duplicate cookie headers, the values are joined together with ; .
/// For all other headers, the values are joined together with ,
external js.JSAny? get headers;
}

extension type JSHttpsResponse._(js.JSObject _) implements js.JSObject {
/// https://expressjs.com/en/4x/api.html#res.send
/// Sends the HTTP response.
/// The body parameter can be a Buffer object, a String, an object, Boolean, or an Array. For example:
external void send([js.JSAny? body]);

/// Ends the response process. This method actually comes from Node core,
/// specifically the response.end() method of http.ServerResponse.
external void end();

/// Sets the HTTP status for the response.
/// It is a chainable alias of Node’s response.statusCode.
external JSHttpsResponse status(int statusCode);

/// Appends the specified value to the HTTP response header field.
/// If the header is not already set, it creates the header with the
/// specified value. The value parameter can be a string or an array.
///
/// Note: calling res.set() after res.append() will reset the previously-set header value.
///
/// res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>'])
/// res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly')
/// res.append('Warning', '199 Miscellaneous warning')
@js.JS('append')
external JSHttpsResponse _setHeader(String field, js.JSAny value);

JSHttpsResponse setHeader(String field, String value) =>
_setHeader(field, value.toJS);
JSHttpsResponse setHeaderList(String field, List<String> values) =>
_setHeader(field, values.map((e) => e.toJS).toList().toJS);
}

extension JSHttpsResponseExt on JSHttpsResponse {}

typedef JSHttpsFunction = js.JSFunction;
typedef _JSHttpsHandler = js.JSFunction;

typedef HttpsHandler = FutureOr<void> Function(
JSHttpsRequest request, JSHttpsResponse response);

typedef JSFirebaseFunction = js.JSFunction;

/// Options that can be set on an onRequest HTTPS function.
///
/// export interface HttpsOptions extends Omit<GlobalOptions, "region">
extension type JSHttpsOptions._(js.JSObject _) implements JSGlobalOptions {
/// Options
external factory JSHttpsOptions(
{String? region,
String? memory,
int? concurrency,
js.JSAny? cors,
int? timeoutSeconds});
}

extension JSHttpsOptionsExt on JSHttpsOptions {
/// string | boolean | RegExp | Array<string | RegExp>
///
/// If true, allows CORS on requests to this function. If this is a string or
/// RegExp, allows requests from domains that match the provided value. If
/// this is an Array, allows requests from domains matching at least one entry
/// of the array. Defaults to true for https.CallableFunction and false
/// otherwise.
external js.JSAny? get cors;
}
97 changes: 70 additions & 27 deletions functions_node/lib/src/node/firebase_functions_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,38 @@ import 'dart:js_interop';

import 'package:tekartik_firebase_functions/firebase_functions.dart';
import 'package:tekartik_firebase_functions_http/firebase_functions_http.dart';
import 'package:tekartik_firebase_functions_node/src/import_common.dart';
import 'package:tekartik_firebase_functions_node/src/node/firebase_functions_https_node_js_interop.dart'
as node;
import 'package:tekartik_firebase_functions_node/src/node/firebase_functions_node_js_interop.dart'
as node;
import 'package:tekartik_firebase_functions_node/src/node/firebase_functions_scheduler_node_js_interop.dart'
as node;

import 'express_http_request_node.dart';

final firebaseFunctionsNode = FirebaseFunctionsNode();

class FirebaseFunctionsNode extends FirebaseFunctionsHttp {
final nativeInstance = node.firebaseFunctionsModule;

@override
void operator []=(String key, FirebaseFunction function) {
nativeInstance[key] = (function as FirebaseFunctionNode).nativeInstance;
}

@override
HttpsFunctions get https => HttpsFunctionsNode(this, nativeInstance.https);

@override
SchedulerFunctions get scheduler =>
SchedulerFunctionsNode(this, nativeInstance.scheduler);
}

class HttpsFunctionsNode with HttpsFunctionsMixin implements HttpsFunctions {
final FirebaseFunctionsNode functions;
final node.JSHttpsFunctions nativeInstance;

HttpsFunctionsNode(this.functions, this.nativeInstance);

@override
Expand All @@ -45,47 +56,79 @@ class HttpsFunctionsNode with HttpsFunctionsMixin implements HttpsFunctions {

//throw UnimplementedError('onRequestV2');
}
} /*
}

class ExpressHttpRequestNode extends ExpressHttpRequestWrapperBase
implements ExpressHttpRequest {
node.HttpsRequest get nativeInstance =>
(implHttpRequest as HttpsRequestNode).nodeHttpRequest
as impl.ExpressHttpRequest;
abstract class FirebaseFunctionNode implements FirebaseFunction {
final FirebaseFunctionsNode firebaseFunctionsNode;

FirebaseFunctionNode(this.firebaseFunctionsNode);

ExpressHttpRequestNode(ExpressHttpRequest httpRequest, Uri rewrittenUri)
: super(HttpRequestNode(httpRequest), rewrittenUri);
node.JSFirebaseFunction get nativeInstance;
}

class HttpsFunctionNode extends FirebaseFunctionNode implements HttpsFunction {
@override
dynamic get body => nativeInstance.body;
final node.JSHttpsFunction nativeInstance;

ExpressHttpResponse? _response;
HttpsFunctionNode(super.firebaseFunctionsNode, this.nativeInstance);
}

node.JSHttpsOptions toNodeHttpsOptions(HttpsOptions httpsOptions) {
return node.JSHttpsOptions(
region: httpsOptions.region, cors: httpsOptions.cors?.toJS);
}

class SchedulerFunctionNode extends FirebaseFunctionNode
implements ScheduleFunction {
@override
ExpressHttpResponse get response => _response ??=
ExpressHttpResponseNode(nativeInstance.response as NodeHttpResponse);
}*/
final node.JSScheduleFunction nativeInstance;

class ExpressHttpResponseNode extends ExpressHttpResponseWrapperBase
implements ExpressHttpResponse {
ExpressHttpResponseNode(super.implHttpResponse);
SchedulerFunctionNode(super.firebaseFunctionsNode, this.nativeInstance);
}

class HttpsFunctionNode extends FirebaseFunctionNode implements HttpsFunction {
node.JSScheduleOptions toNodeScheduleOptions(ScheduleOptions options) {
return node.JSScheduleOptions(
region: options.region,
schedule: options.schedule,
timeZone: options.timeZone,
memory: options.memory,
timeoutSeconds: options.timeoutSeconds);
}

class SchedulerFunctionsNode
with SchedulerFunctionsDefaultMixin
implements SchedulerFunctions {
final FirebaseFunctionsNode functions;
final node.JSSchedulerFunctions nativeInstance;

SchedulerFunctionsNode(this.functions, this.nativeInstance);

@override
final node.JSHttpsFunction nativeInstance;
ScheduleFunction onSchedule(
ScheduleOptions scheduleOptions, ScheduleHandler handler) {
FutureOr<void> handleRequest(node.JSScheduledEvent data) {
var scheduleEvent = ScheduleEventNode(data);
return handler(scheduleEvent);
}

HttpsFunctionNode(super.firebaseFunctionsNode, this.nativeInstance);
return SchedulerFunctionNode(
functions,
nativeInstance.onSchedule(
options: toNodeScheduleOptions(scheduleOptions),
handler: handleRequest));
}
}

abstract class FirebaseFunctionNode implements FirebaseFunction {
final FirebaseFunctionsNode firebaseFunctionsNode;
class ScheduleEventNode
with SchedulerEventDefaultMixin
implements ScheduleEvent {
final node.JSScheduledEvent nativeInstance;

FirebaseFunctionNode(this.firebaseFunctionsNode);
node.JSFirebaseFunction get nativeInstance;
}
ScheduleEventNode(this.nativeInstance);

node.JSHttpsOptions toNodeHttpsOptions(HttpsOptions httpsOptions) {
return node.JSHttpsOptions(
region: httpsOptions.region, cors: httpsOptions.cors?.toJS);
@override
String? get jobName => nativeInstance.jobName;

@override
String? get scheduleTime => nativeInstance.scheduleTime;
}
Loading

0 comments on commit 3bf8774

Please sign in to comment.