protected ParameterDescriptor ParseQueueTrigger(JObject trigger, Type triggerParameterType = null)
        {
            if (triggerParameterType == null)
            {
                triggerParameterType = typeof(string);
            }

            ConstructorInfo ctorInfo = typeof(QueueTriggerAttribute).GetConstructor(new Type[] { typeof(string) });
            string queueName = (string)trigger["queueName"];
            CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(ctorInfo, new object[] { queueName });

            string parameterName = (string)trigger["name"];
            ParameterDescriptor triggerParameter = new ParameterDescriptor
            {
                Name = parameterName,
                Type = triggerParameterType,
                Attributes = ParameterAttributes.None,
                CustomAttributes = new Collection<CustomAttributeBuilder>
                {
                    attributeBuilder
                }
            };

            return triggerParameter;
        }
        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);
        }
        protected ParameterDescriptor ParseServiceBusTrigger(JObject trigger, Type triggerParameterType = null)
        {
            if (triggerParameterType == null)
            {
                triggerParameterType = typeof(string);
            }

            string queueName = (string)trigger["queueName"];
            string topicName = (string)trigger["topicName"];
            string subscriptionName = (string)trigger["subscriptionName"];

            string accessRightsValue = (string)trigger["accessRights"];
            AccessRights accessRights = AccessRights.Manage;
            if (!string.IsNullOrEmpty(accessRightsValue))
            {
                AccessRights parsed;
                if (Enum.TryParse<AccessRights>(accessRightsValue, true, out parsed))
                {
                    accessRights = parsed;
                }
            }

            CustomAttributeBuilder attributeBuilder = null;
            if (!string.IsNullOrEmpty(topicName) && !string.IsNullOrEmpty(subscriptionName))
            {
                ConstructorInfo ctorInfo = typeof(ServiceBusTriggerAttribute).GetConstructor(new Type[] { typeof(string), typeof(string), typeof(AccessRights) });
                attributeBuilder = new CustomAttributeBuilder(ctorInfo, new object[] { topicName, subscriptionName, accessRights });
            }
            else if (!string.IsNullOrEmpty(queueName))
            {
                ConstructorInfo ctorInfo = typeof(ServiceBusTriggerAttribute).GetConstructor(new Type[] { typeof(string), typeof(AccessRights) });
                attributeBuilder = new CustomAttributeBuilder(ctorInfo, new object[] { queueName, accessRights });
            }
            else
            {
                throw new InvalidOperationException("Invalid servicebus trigger configuration.");
            }

            string parameterName = (string)trigger["name"];
            ParameterDescriptor triggerParameter = new ParameterDescriptor
            {
                Name = parameterName,
                Type = triggerParameterType,
                Attributes = ParameterAttributes.None,
                CustomAttributes = new Collection<CustomAttributeBuilder>
                {
                    attributeBuilder
                }
            };

            return triggerParameter;
        }
        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;
        }