in src/WebJobs.Script.WebHost/Management/WebFunctionsManager.cs [95:172]
public async Task<(bool Success, bool ConfigChanged, FunctionMetadataResponse Response)> CreateOrUpdate(string name, FunctionMetadataResponse functionMetadata, HttpRequest request)
{
var hostOptions = _applicationHostOptions.CurrentValue.ToHostOptions();
var configChanged = false;
var functionDir = Path.Combine(hostOptions.RootScriptPath, name);
// Make sure the function folder exists
if (!FileUtility.DirectoryExists(functionDir))
{
// Cleanup any leftover artifacts from a function with the same name before.
DeleteFunctionArtifacts(functionMetadata);
Directory.CreateDirectory(functionDir);
}
string newConfig = null;
string configPath = Path.Combine(functionDir, ScriptConstants.FunctionMetadataFileName);
string dataFilePath = Extensions.FunctionMetadataExtensions.GetTestDataFilePath(name, hostOptions);
// If files are included, write them out
if (functionMetadata?.Files != null)
{
// If the config is passed in the file collection, save it and don't process it as a file
if (functionMetadata.Files.TryGetValue(ScriptConstants.FunctionMetadataFileName, out newConfig))
{
functionMetadata.Files.Remove(ScriptConstants.FunctionMetadataFileName);
}
// Delete all existing files in the directory. This will also delete current function.json, but it gets recreated below
FileUtility.DeleteDirectoryContentsSafe(functionDir);
await functionMetadata
.Files
.Select(e => FileUtility.WriteAsync(Path.Combine(functionDir, e.Key), e.Value))
.WhenAll();
}
// Get the config (if it was not already passed in as a file)
if (newConfig == null && functionMetadata?.Config != null)
{
newConfig = JsonConvert.SerializeObject(functionMetadata?.Config, Formatting.Indented);
}
// Get the current config, if any
string currentConfig = null;
if (FileUtility.FileExists(configPath))
{
currentConfig = await FileUtility.ReadAsync(configPath);
}
// Save the file and set changed flag is it has changed. This helps optimize the syncTriggers call
if (newConfig != currentConfig)
{
await FileUtility.WriteAsync(configPath, newConfig);
configChanged = true;
}
if (functionMetadata.TestData != null && !_hostingConfigOptions.CurrentValue.IsTestDataSuppressionEnabled)
{
await FileUtility.WriteAsync(dataFilePath, functionMetadata.TestData);
}
// Using HostFunctionMetadataProvider instead of IFunctionMetadataManager. More details logged here https://github.com/Azure/azure-functions-host/issues/10691
var metadata = (await _hostFunctionMetadataProvider.GetFunctionMetadataAsync(_languageWorkerOptions.CurrentValue.WorkerConfigs, true))
.FirstOrDefault(metadata => Utility.FunctionNamesMatch(metadata.Name, name));
bool success = false;
FunctionMetadataResponse functionMetadataResult = null;
if (metadata != null)
{
functionMetadataResult = await GetFunctionMetadataResponseAsync(metadata, hostOptions, request, _hostingConfigOptions.CurrentValue.IsTestDataSuppressionEnabled);
success = true;
}
// we need to sync triggers if config changed, or the files changed
await _functionsSyncManager.TrySyncTriggersAsync();
return (success, configChanged, functionMetadataResult);
}