private AzFunctionType GetAzFunctionType(ReadOnlyBindingInfo bindingInfo) { switch (bindingInfo.Type) { case OrchestrationTrigger: return(AzFunctionType.OrchestrationFunction); case ActivityTrigger: return(AzFunctionType.ActivityFunction); default: // All other triggers are considered regular functions return(AzFunctionType.RegularFunction); } }
/// <summary> /// Set the 'ReturnValue' and 'OutputData' based on the invocation results appropriately. /// </summary> private void BindOutputFromResult(InvocationResponse response, AzFunctionInfo functionInfo, Hashtable results) { switch (functionInfo.Type) { case AzFunctionType.RegularFunction: // Set out binding data and return response to be sent back to host foreach (KeyValuePair <string, ReadOnlyBindingInfo> binding in functionInfo.OutputBindings) { string outBindingName = binding.Key; ReadOnlyBindingInfo bindingInfo = binding.Value; object outValue = results[outBindingName]; object transformedValue = Utils.TransformOutBindingValueAsNeeded(outBindingName, bindingInfo, outValue); TypedData dataToUse = transformedValue.ToTypedData(); // if one of the bindings is '$return' we need to set the ReturnValue if (string.Equals(outBindingName, AzFunctionInfo.DollarReturn, StringComparison.OrdinalIgnoreCase)) { response.ReturnValue = dataToUse; continue; } ParameterBinding paramBinding = new ParameterBinding() { Name = outBindingName, Data = dataToUse }; response.OutputData.Add(paramBinding); } break; case AzFunctionType.OrchestrationFunction: case AzFunctionType.ActivityFunction: response.ReturnValue = results[AzFunctionInfo.DollarReturn].ToTypedData(); break; default: throw new InvalidOperationException("Unreachable code."); } }
/// <summary> /// Construct an object of AzFunctionInfo from the 'RpcFunctionMetadata'. /// Necessary validations are done on the metadata and script. /// </summary> internal AzFunctionInfo(RpcFunctionMetadata metadata) { FuncName = metadata.Name; FuncDirectory = metadata.Directory; EntryPoint = metadata.EntryPoint; ScriptPath = metadata.ScriptFile; // Support 'entryPoint' only if 'scriptFile' is a .psm1 file; // Support .psm1 'scriptFile' only if 'entryPoint' is specified. bool isScriptFilePsm1 = ScriptPath.EndsWith(".psm1", StringComparison.OrdinalIgnoreCase); bool entryPointNotDefined = string.IsNullOrEmpty(EntryPoint); if (entryPointNotDefined) { if (isScriptFilePsm1) { throw new ArgumentException(PowerShellWorkerStrings.RequireEntryPointForScriptModule); } } else if (!isScriptFilePsm1) { throw new ArgumentException(PowerShellWorkerStrings.InvalidEntryPointForScriptFile); } // Get the parameter names of the script or function. var psScriptParams = GetParameters(ScriptPath, EntryPoint, out ScriptBlockAst scriptAst); FuncParameters = new ReadOnlyDictionary <string, PSScriptParamInfo>(psScriptParams); var parametersCopy = new Dictionary <string, PSScriptParamInfo>(psScriptParams, StringComparer.OrdinalIgnoreCase); HasTriggerMetadataParam = parametersCopy.Remove(TriggerMetadata); HasTraceContextParam = parametersCopy.Remove(TraceContext); var allBindings = new Dictionary <string, ReadOnlyBindingInfo>(StringComparer.OrdinalIgnoreCase); var inputBindings = new Dictionary <string, ReadOnlyBindingInfo>(StringComparer.OrdinalIgnoreCase); var outputBindings = new Dictionary <string, ReadOnlyBindingInfo>(StringComparer.OrdinalIgnoreCase); var inputsMissingFromParams = new List <string>(); foreach (var binding in metadata.Bindings) { string bindingName = binding.Key; var bindingInfo = new ReadOnlyBindingInfo(binding.Value); allBindings.Add(bindingName, bindingInfo); if (bindingInfo.Direction == BindingInfo.Types.Direction.In) { Type = GetAzFunctionType(bindingInfo); inputBindings.Add(bindingName, bindingInfo); // If the input binding name is in the set, we remove it; // otherwise, the binding name is missing from the params. if (!parametersCopy.Remove(bindingName)) { inputsMissingFromParams.Add(bindingName); } } else if (bindingInfo.Direction == BindingInfo.Types.Direction.Out) { if (bindingInfo.Type == OrchestrationClient) { OrchestrationClientBindingName = bindingName; } outputBindings.Add(bindingName, bindingInfo); } else { // PowerShell doesn't support the 'InOut' type binding throw new InvalidOperationException(string.Format(PowerShellWorkerStrings.InOutBindingNotSupported, bindingName)); } } if (inputsMissingFromParams.Count != 0 || parametersCopy.Count != 0) { StringBuilder stringBuilder = new StringBuilder(); foreach (string inputBindingName in inputsMissingFromParams) { stringBuilder.AppendFormat(PowerShellWorkerStrings.MissingParameter, inputBindingName).AppendLine(); } foreach (string param in parametersCopy.Keys) { stringBuilder.AppendFormat(PowerShellWorkerStrings.UnknownParameter, param).AppendLine(); } string errorMsg = stringBuilder.ToString(); throw new InvalidOperationException(errorMsg); } if (entryPointNotDefined && scriptAst.ScriptRequirements == null) { // If the function script is a '.ps1' file that doesn't have '#requires' defined, // then we get the script block and will deploy it as a PowerShell function in the // global scope of each Runspace, so as to avoid hitting the disk every invocation. FuncScriptBlock = scriptAst.GetScriptBlock(); DeployedPSFuncName = $"_{FuncName}_"; } AllBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(allBindings); InputBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(inputBindings); OutputBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(outputBindings); }
/// <summary> /// Construct an object of AzFunctionInfo from the 'RpcFunctionMetadata'. /// Necessary validations are done on the metadata and script. /// </summary> internal AzFunctionInfo(RpcFunctionMetadata metadata) { FuncName = metadata.Name; FuncDirectory = metadata.Directory; EntryPoint = metadata.EntryPoint; ScriptPath = metadata.ScriptFile; // Support 'entryPoint' only if 'scriptFile' is a .psm1 file; // Support .psm1 'scriptFile' only if 'entryPoint' is specified. bool isScriptFilePsm1 = ScriptPath.EndsWith(".psm1", StringComparison.OrdinalIgnoreCase); if (string.IsNullOrEmpty(EntryPoint)) { if (isScriptFilePsm1) { throw new ArgumentException(PowerShellWorkerStrings.RequireEntryPointForScriptModule); } } else if (!isScriptFilePsm1) { throw new ArgumentException(PowerShellWorkerStrings.InvalidEntryPointForScriptFile); } // Get the parameter names of the script or function. var psScriptParams = GetParameters(ScriptPath, EntryPoint); FuncParameters = new ReadOnlyDictionary <string, PSScriptParamInfo>(psScriptParams); var parametersCopy = new Dictionary <string, PSScriptParamInfo>(psScriptParams, StringComparer.OrdinalIgnoreCase); HasTriggerMetadataParam = parametersCopy.Remove(TriggerMetadata); var allBindings = new Dictionary <string, ReadOnlyBindingInfo>(StringComparer.OrdinalIgnoreCase); var inputBindings = new Dictionary <string, ReadOnlyBindingInfo>(StringComparer.OrdinalIgnoreCase); var outputBindings = new Dictionary <string, ReadOnlyBindingInfo>(StringComparer.OrdinalIgnoreCase); var inputsMissingFromParams = new List <string>(); foreach (var binding in metadata.Bindings) { string bindingName = binding.Key; var bindingInfo = new ReadOnlyBindingInfo(binding.Value); allBindings.Add(bindingName, bindingInfo); if (bindingInfo.Direction == BindingInfo.Types.Direction.In) { Type = GetAzFunctionType(bindingInfo); inputBindings.Add(bindingName, bindingInfo); // If the input binding name is in the set, we remove it; // otherwise, the binding name is missing from the params. if (!parametersCopy.Remove(bindingName)) { inputsMissingFromParams.Add(bindingName); } } else if (bindingInfo.Direction == BindingInfo.Types.Direction.Out) { outputBindings.Add(bindingName, bindingInfo); } else { // PowerShell doesn't support the 'InOut' type binding throw new InvalidOperationException(string.Format(PowerShellWorkerStrings.InOutBindingNotSupported, bindingName)); } } if (inputsMissingFromParams.Count != 0 || parametersCopy.Count != 0) { StringBuilder stringBuilder = new StringBuilder(); foreach (string inputBindingName in inputsMissingFromParams) { stringBuilder.AppendFormat(PowerShellWorkerStrings.MissingParameter, inputBindingName).AppendLine(); } foreach (string param in parametersCopy.Keys) { stringBuilder.AppendFormat(PowerShellWorkerStrings.UnknownParameter, param).AppendLine(); } string errorMsg = stringBuilder.ToString(); throw new InvalidOperationException(errorMsg); } AllBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(allBindings); InputBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(inputBindings); OutputBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(outputBindings); }
/// <summary> /// Construct an object of AzFunctionInfo from the 'RpcFunctionMetadata'. /// Necessary validations are done on the metadata and script. /// </summary> internal AzFunctionInfo(RpcFunctionMetadata metadata) { FuncName = metadata.Name; FuncDirectory = metadata.Directory; EntryPoint = metadata.EntryPoint; ScriptPath = metadata.ScriptFile; // Support 'entryPoint' only if 'scriptFile' is a .psm1 file; // Support .psm1 'scriptFile' only if 'entryPoint' is specified. bool isScriptFilePsm1 = ScriptPath.EndsWith(".psm1", StringComparison.OrdinalIgnoreCase); if (string.IsNullOrEmpty(EntryPoint)) { if (isScriptFilePsm1) { throw new ArgumentException($"The 'entryPoint' property needs to be specified when 'scriptFile' points to a PowerShell module script file (.psm1)."); } } else if (!isScriptFilePsm1) { throw new ArgumentException($"The 'entryPoint' property is supported only if 'scriptFile' points to a PowerShell module script file (.psm1)."); } // Get the parameter names of the script or function. FuncParameters = GetParameters(ScriptPath, EntryPoint); var parametersCopy = new HashSet <string>(FuncParameters, StringComparer.OrdinalIgnoreCase); parametersCopy.Remove(TriggerMetadata); var allBindings = new Dictionary <string, ReadOnlyBindingInfo>(); var inputBindings = new Dictionary <string, ReadOnlyBindingInfo>(); var outputBindings = new Dictionary <string, ReadOnlyBindingInfo>(); var inputsMissingFromParams = new List <string>(); foreach (var binding in metadata.Bindings) { string bindingName = binding.Key; var bindingInfo = new ReadOnlyBindingInfo(binding.Value); allBindings.Add(bindingName, bindingInfo); if (bindingInfo.Direction == BindingInfo.Types.Direction.In) { Type = GetAzFunctionType(bindingInfo); inputBindings.Add(bindingName, bindingInfo); // If the input binding name is in the set, we remove it; // otherwise, the binding name is missing from the params. if (!parametersCopy.Remove(bindingName)) { inputsMissingFromParams.Add(bindingName); } } else if (bindingInfo.Direction == BindingInfo.Types.Direction.Out) { outputBindings.Add(bindingName, bindingInfo); } else { // PowerShell doesn't support the 'InOut' type binding throw new InvalidOperationException($"The binding '{bindingName}' is declared with 'InOut' direction, which is not supported by PowerShell functions."); } } if (inputsMissingFromParams.Count != 0 || parametersCopy.Count != 0) { StringBuilder stringBuilder = new StringBuilder(); foreach (string inputBindingName in inputsMissingFromParams) { stringBuilder.AppendLine($"No parameter defined in the script or function for the input binding '{inputBindingName}'."); } foreach (string param in parametersCopy) { stringBuilder.AppendLine($"No input binding defined for the parameter '{param}' that is declared in the script or function."); } string errorMsg = stringBuilder.ToString(); throw new InvalidOperationException(errorMsg); } AllBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(allBindings); InputBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(inputBindings); OutputBindings = new ReadOnlyDictionary <string, ReadOnlyBindingInfo>(outputBindings); }