public async Task Generate_EndToEnd()
        {
            // construct our TimerTrigger attribute ([TimerTrigger("00:00:02", RunOnStartup = true)])
            Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>();
            ParameterDescriptor parameter = new ParameterDescriptor("timerInfo", typeof(TimerInfo));
            ConstructorInfo ctorInfo = typeof(TimerTriggerAttribute).GetConstructor(new Type[] { typeof(string) });
            PropertyInfo runOnStartupProperty = typeof(TimerTriggerAttribute).GetProperty("RunOnStartup");
            CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(
                ctorInfo, 
                new object[] { "00:00:02" },
                new PropertyInfo[] { runOnStartupProperty },
                new object[] { true });
            parameter.CustomAttributes.Add(attributeBuilder);
            parameters.Add(parameter);

            // create the FunctionDefinition
            TestInvoker invoker = new TestInvoker();
            FunctionDescriptor function = new FunctionDescriptor("TimerFunction", invoker, parameters);
            Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>();
            functions.Add(function);

            // generate the Type
            Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", functions);

            // verify the generated function
            MethodInfo method = functionType.GetMethod("TimerFunction");
            ParameterInfo triggerParameter = method.GetParameters()[0];
            TimerTriggerAttribute triggerAttribute = triggerParameter.GetCustomAttribute<TimerTriggerAttribute>();
            Assert.NotNull(triggerAttribute);

            // start the JobHost which will start running the timer function
            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator = new TypeLocator(functionType)
            };
            config.UseTimers();
            JobHost host = new JobHost(config);

            await host.StartAsync();
            await Task.Delay(3000);
            await host.StopAsync();

            // verify our custom invoker was called
            Assert.True(invoker.InvokeCount >= 2);
        }
        public override bool TryCreate(FunctionFolderInfo functionFolderInfo, out FunctionDescriptor functionDescriptor)
        {
            functionDescriptor = null;

            // name might point to a single file, or a module
            string extension = Path.GetExtension(functionFolderInfo.Source).ToLower();
            if (!(extension == ".js" || string.IsNullOrEmpty(extension)))
            {
                return false;
            }

            // parse the bindings
            JObject bindings = (JObject)functionFolderInfo.Configuration["bindings"];
            JArray inputs = (JArray)bindings["input"];
            Collection<Binding> inputBindings = Binding.GetBindings(inputs, FileAccess.Read);

            JArray outputs = (JArray)bindings["output"];
            Collection<Binding> outputBindings = Binding.GetBindings(outputs, FileAccess.Write);

            JObject trigger = (JObject)inputs.FirstOrDefault(p => ((string)p["type"]).ToLowerInvariant().EndsWith("trigger"));
            string triggerType = (string)trigger["type"];

            string triggerParameterName = (string)trigger["name"];
            if (string.IsNullOrEmpty(triggerParameterName))
            {
                // default the name to simply 'input'
                trigger["name"] = triggerParameterName = "input";
            }

            NodeFunctionInvoker invoker = new NodeFunctionInvoker(triggerParameterName, functionFolderInfo.Source, inputBindings, outputBindings);

            ParameterDescriptor triggerParameter = null;
            switch (triggerType)
            {
                case "queueTrigger":
                    triggerParameter = ParseQueueTrigger(trigger);
                    break;
                case "blobTrigger":
                    triggerParameter = ParseBlobTrigger(trigger);
                    break;
                case "serviceBusTrigger":
                    triggerParameter = ParseServiceBusTrigger(trigger);
                    break;
                case "timerTrigger":
                    triggerParameter = ParseTimerTrigger(trigger, typeof(TimerInfo));
                    break;
                case "webHookTrigger":
                    triggerParameter = ParseWebHookTrigger(trigger);
                    break;
            }

            Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>();
            parameters.Add(triggerParameter);

            // Add a TraceWriter for logging
            parameters.Add(new ParameterDescriptor("log", typeof(TraceWriter)));

            // Add an IBinder to support the binding programming model
            parameters.Add(new ParameterDescriptor("binder", typeof(IBinder)));

            functionDescriptor = new FunctionDescriptor(functionFolderInfo.Name, invoker, parameters);

            return true;
        }
 public abstract bool TryCreate(FunctionFolderInfo functionFolderInfo, out FunctionDescriptor functionDescriptor);
 public abstract bool TryCreate(JObject function, out FunctionDescriptor functionDescriptor);
        public override bool TryCreate(JObject function, out FunctionDescriptor functionDescriptor)
        {
            functionDescriptor = null;

            string name = (string)function["name"];
            if (string.IsNullOrEmpty(name))
            {
                // if a method name isn't explicitly provided, derive it
                // from the script file name
                string source = (string)function["source"];
                name = Path.GetFileNameWithoutExtension(source);
            }
            MethodInfo method = FindMethod(name);
            if (method == null)
            {
                throw new InvalidOperationException(string.Format("Unable to bind to method '{0}'", name));
            }

            MethodInvoker invoker = new MethodInvoker(method);

            JObject trigger = (JObject)function["trigger"];
            string triggerType = (string)trigger["type"];

            // TODO: match based on name? Or is positional convention OK?
            ParameterInfo[] sourceParameters = invoker.Target.GetParameters();
            ParameterInfo targetTriggerParameter = sourceParameters[0];
            Type triggerParameterType = targetTriggerParameter.ParameterType;

            string parameterName = (string)trigger["name"];
            if (string.IsNullOrEmpty(parameterName))
            {
                // default the name to the actual source parameter name
                trigger["name"] = targetTriggerParameter.Name;
            }

            ParameterDescriptor triggerParameter = null;
            switch (triggerType)
            {
                case "queue":
                    triggerParameter = ParseQueueTrigger(trigger, triggerParameterType);
                    break;
                case "blob":
                    triggerParameter = ParseBlobTrigger(trigger, triggerParameterType);
                    break;
                case "serviceBus":
                    triggerParameter = ParseServiceBusTrigger(trigger, triggerParameterType);
                    break;
                case "timer":
                    triggerParameter = ParseTimerTrigger(trigger, triggerParameterType);
                    break;
                case "webHook":
                    triggerParameter = ParseWebHookTrigger(trigger, typeof(HttpRequestMessage));
                    break;
            }

            Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>();
            parameters.Add(triggerParameter);

            // now add any additional parameters found on the source method
            // TODO: restrict to certain types only?
            foreach (ParameterInfo sourceParameter in sourceParameters.Skip(1))
            {
                ParameterDescriptor parameter = new ParameterDescriptor
                {
                    Name = sourceParameter.Name,
                    Type = sourceParameter.ParameterType
                };
                parameters.Add(parameter);
            }

            functionDescriptor = new FunctionDescriptor
            {
                Name = name,
                Invoker = invoker,
                Parameters = parameters
            };

            return true;
        }