diff --git a/build.fsx b/build.fsx index 0f4e64dfd..9464d94c2 100644 --- a/build.fsx +++ b/build.fsx @@ -161,6 +161,7 @@ Target "GenerateZipToSign" (fun _ -> !! (buildDir @@ "/**/Microsoft.Azure.*.dll") ++ (buildDir @@ "func.exe") + ++ (buildDir @@ "azurefunctions/functions.js") ++ (buildDir @@ "azurefunctions/http/request.js") ++ (buildDir @@ "azurefunctions/http/response.js") |> notSigned @@ -246,6 +247,7 @@ Target "WaitForSigning" (fun _ -> match signed with | Success file -> Unzip buildDir file + MoveFile (buildDir @@ "azurefunctions/") (buildDir @@ "functions.js") MoveFile (buildDir @@ "azurefunctions/http/") (buildDir @@ "request.js") MoveFile (buildDir @@ "azurefunctions/http/") (buildDir @@ "response.js") | Failure e -> targetError e null |> ignore diff --git a/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj b/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj index 27c487bb5..12a26162d 100644 --- a/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj +++ b/src/Azure.Functions.Cli/Azure.Functions.Cli.csproj @@ -828,6 +828,9 @@ edge\edge.js PreserveNewest + + PreserveNewest + Always diff --git a/src/Azure.Functions.Cli/azurefunctions/functions.js b/src/Azure.Functions.Cli/azurefunctions/functions.js new file mode 100644 index 000000000..b9b320508 --- /dev/null +++ b/src/Azure.Functions.Cli/azurefunctions/functions.js @@ -0,0 +1,142 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +var util = require('util'); +var process = require('process'); +var request = require('./http/request'); +var response = require('./http/response'); + +module.exports = { + globalInitialization: globalInitialization, + clearRequireCache: clearRequireCache, + createFunction: createFunction +}; + +function globalInitialization(context, callback) { + process.on('uncaughtException', function (err) { + context.handleUncaughtException(err.stack); + }); + callback(); +} + +function clearRequireCache(context, callback) { + Object.keys(require.cache).forEach(function (key) { + delete require.cache[key]; + }); + callback(); +} + +function createFunction(f) { + return function (context, callback) { + // TEMP HACK: workaround for https://github.com/tjanczuk/edge/issues/325 + setImmediate(() => { }); + + f = getEntryPoint(f, context); + + // configure loggers + var origLog = context.log; + var logLevel = function (traceLevel) { + return function () { + var message = util.format.apply(null, arguments); + origLog({ lvl: traceLevel, msg: message }); + }; + }; + // set default log to 'info' + var log = logLevel(3); + ['error', 'warn', 'info', 'verbose'].forEach((level, index) => { + var traceLevel = index + 1; + log[level] = logLevel(traceLevel); + }); + context.log = log; + + var origMetric = context._metric; + delete context._metric; + context.log.metric = function (name, value, properties) { + origMetric({ name: name, value: value, properties: properties}); + }; + + context.done = function (err, returnValue) { + if (context._done) { + if (context._promise) { + context.log("Error: Choose either to return a promise or call 'done'. Do not use both in your script."); + } else { + context.log("Error: 'done' has already been called. Please check your script for extraneous calls to 'done'."); + } + return; + } + context._done = true; + + if (err) { + callback(err); + } + else { + if (context.res && context.bindings.res === undefined) { + context.bindings.res = context.res; + } + + // because Edge.JS interop doesn't flow new values added to objects, + // we capture the binding values and pass them back as part of the + // result + var bindingValues = {}; + for (var name in context.bindings) { + bindingValues[name] = context.bindings[name]; + } + + var result = { + returnValue: returnValue, + bindingValues: bindingValues + }; + callback(null, result); + } + }; + + var inputs = context._inputs; + inputs.unshift(context); + delete context._inputs; + + var lowercaseTrigger = context._triggerType && context._triggerType.toLowerCase(); + switch (lowercaseTrigger) { + case "httptrigger": + context.req = request(context); + context.res = response(context); + break; + } + delete context._triggerType; + + var result = f.apply(null, inputs); + if (result && util.isFunction(result.then)) { + context._promise = true; + result.then((result) => context.done(null, result)) + .catch((err) => context.done(err)); + } + }; +} + +function getEntryPoint(f, context) { + if (util.isObject(f)) { + if (context._entryPoint) { + // the module exports multiple functions + // and an explicit entry point was named + f = f[context._entryPoint]; + delete context._entryPoint; + } + else if (Object.keys(f).length === 1) { + // a single named function was exported + var name = Object.keys(f)[0]; + f = f[name]; + } + else { + // finally, see if there is an exported function named + // 'run' or 'index' by convention + f = f.run || f.index; + } + } + + if (!util.isFunction(f)) { + throw "Unable to determine function entry point. If multiple functions are exported, " + + "you must indicate the entry point, either by naming it 'run' or 'index', or by naming it " + + "explicitly via the 'entryPoint' metadata property."; + } + + return f; +} \ No newline at end of file