├── DurableFunctionsSample ├── Properties │ ├── serviceDependencies.json │ ├── serviceDependencies.local.json │ ├── PublishProfiles │ │ └── DurableFunctionsSampleTVA - Zip Deploy.pubxml │ └── ServiceDependencies │ │ └── DurableFunctionsSampleTVA - Zip Deploy │ │ └── profile.arm.json ├── host.json ├── DurableFunctionsSample.csproj ├── OrchestrationSample.cs └── .gitignore ├── .idea └── .idea.DurableFunctionsSample │ └── .idea │ ├── encodings.xml │ ├── vcs.xml │ ├── projectSettingsUpdater.xml │ ├── indexLayout.xml │ ├── git_toolbox_prj.xml │ └── workspace.xml └── DurableFunctionsSample.sln /DurableFunctionsSample/Properties/serviceDependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "storage1": { 4 | "type": "storage", 5 | "connectionId": "AzureWebJobsStorage" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /DurableFunctionsSample/Properties/serviceDependencies.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "storage1": { 4 | "type": "storage.emulator", 5 | "connectionId": "AzureWebJobsStorage" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /.idea/.idea.DurableFunctionsSample/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/.idea.DurableFunctionsSample/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.idea.DurableFunctionsSample/.idea/projectSettingsUpdater.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/.idea.DurableFunctionsSample/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DurableFunctionsSample/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingExcludedTypes": "Request", 6 | "samplingSettings": { 7 | "isEnabled": true 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /.idea/.idea.DurableFunctionsSample/.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /DurableFunctionsSample/DurableFunctionsSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | v3 5 | 10 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | PreserveNewest 15 | 16 | 17 | PreserveNewest 18 | Never 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /DurableFunctionsSample/Properties/PublishProfiles/DurableFunctionsSampleTVA - Zip Deploy.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | ZipDeploy 8 | AzureWebSite 9 | Release 10 | Any CPU 11 | https://durablefunctionssampletva.azurewebsites.net 12 | False 13 | /subscriptions/1a938190-e8b8-4d96-8f61-f2606002a079/resourcegroups/learn-3c8ba177-e548-4063-99de-901a21d0eca9/providers/Microsoft.Web/sites/DurableFunctionsSampleTVA 14 | $DurableFunctionsSampleTVA 15 | <_SavePWD>True 16 | https://durablefunctionssampletva.scm.azurewebsites.net/ 17 | 18 | -------------------------------------------------------------------------------- /DurableFunctionsSample.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30611.23 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DurableFunctionsSample", "DurableFunctionsSample\DurableFunctionsSample.csproj", "{C83377DB-E03B-4D53-B80D-027AC56E66E0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C83377DB-E03B-4D53-B80D-027AC56E66E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C83377DB-E03B-4D53-B80D-027AC56E66E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C83377DB-E03B-4D53-B80D-027AC56E66E0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C83377DB-E03B-4D53-B80D-027AC56E66E0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B88880CD-F516-4CD7-954D-1D324AF5344A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /DurableFunctionsSample/OrchestrationSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | using Microsoft.Azure.WebJobs; 8 | using Microsoft.Azure.WebJobs.Extensions.DurableTask; 9 | using Microsoft.Azure.WebJobs.Extensions.Http; 10 | using Microsoft.Extensions.Logging; 11 | using Microsoft.WindowsAzure.Storage.Blob; 12 | 13 | namespace DurableFunctionsSample 14 | { 15 | public static class OrchestrationSample 16 | { 17 | [FunctionName("OrchestrationSample")] 18 | public static async Task RunOrchestrator( 19 | [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) 20 | { 21 | string endpoint = context.GetInput().ToString(); 22 | var request = new DurableHttpRequest(HttpMethod.Get, new Uri(endpoint)); 23 | DurableHttpResponse endpointResponse = await context.CallHttpAsync(request); 24 | if (endpointResponse.StatusCode != HttpStatusCode.OK) 25 | { 26 | throw new ArgumentException($"Failed to contact endpoint: {endpointResponse.StatusCode}: {endpointResponse.Content}"); 27 | } 28 | log.LogInformation("Information retrieved from endpoint = {EndpointResponseContent}", endpointResponse.Content); 29 | 30 | string[] words = endpointResponse.Content.Split(" "); 31 | log.LogInformation("Words c{name}ount = {Count}", "ARG0", words.Count()); 32 | 33 | var entityId = new EntityId("OrchestrationSample_Counter", "charCounter"); 34 | context.SignalEntity(entityId, "reset"); 35 | 36 | foreach (string word in words) 37 | { 38 | context.SignalEntity(entityId, "Add", word); 39 | } 40 | 41 | await context.CallActivityAsync("OrchestrationSample_LogBlob", endpoint.Replace("/", "bar")); 42 | int count = await context.CallEntityAsync(entityId, "Get"); 43 | 44 | return $"Endpoint: {endpoint} has the total of {count} chars"; 45 | } 46 | 47 | [FunctionName("OrchestrationSample_LogBlob")] 48 | public static async Task LogBlobAsync([ActivityTrigger] string name, [Blob("sample-blob/{name}", FileAccess.Write)] CloudBlockBlob blobStream, ILogger log) 49 | { 50 | await blobStream.UploadTextAsync(DateTime.UtcNow.ToString()); 51 | log.LogInformation("Blob Created {UtcNow}", DateTime.UtcNow); 52 | } 53 | 54 | [FunctionName("OrchestrationSample_Counter")] 55 | public static void Counter([EntityTrigger] IDurableEntityContext ctx, ILogger log) 56 | { 57 | log.LogInformation("Entity operation= {CtxOperationName}", ctx.OperationName); 58 | 59 | switch (ctx.OperationName.ToLowerInvariant()) 60 | { 61 | case "add": 62 | var sum = ctx.GetInput().Length - ctx.GetInput().Count(char.IsWhiteSpace); 63 | ctx.SetState(ctx.GetState() + sum); 64 | break; 65 | case "reset": 66 | ctx.SetState(0); 67 | break; 68 | case "get": 69 | ctx.Return(ctx.GetState()); 70 | break; 71 | } 72 | } 73 | 74 | [FunctionName("OrchestrationSample_HttpStart")] 75 | public static async Task HttpStart( 76 | [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req, 77 | [DurableClient] IDurableOrchestrationClient starter, ILogger log) 78 | { 79 | var qs = req.RequestUri.ParseQueryString(); 80 | object endpoint = qs.Get("endpointUri"); 81 | string instanceId = await starter.StartNewAsync("OrchestrationSample", endpoint); 82 | log.LogInformation("Started orchestration with ID = \'{InstanceId}\'", instanceId); 83 | log.LogInformation("Endpoint = \'{Endpoint}\'", endpoint); 84 | return starter.CreateCheckStatusResponse(req, instanceId); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /.idea/.idea.DurableFunctionsSample/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 19 | 28 | 29 | 30 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | C:\Users\Fazrin\AppData\Roaming\Subversion 56 | 57 | 58 | 63 | -------------------------------------------------------------------------------- /DurableFunctionsSample/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /DurableFunctionsSample/Properties/ServiceDependencies/DurableFunctionsSampleTVA - Zip Deploy/profile.arm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "metadata": { 5 | "_dependencyType": "function.windows.consumption" 6 | }, 7 | "parameters": { 8 | "resourceGroupName": { 9 | "type": "string", 10 | "defaultValue": "learn-3c8ba177-e548-4063-99de-901a21d0eca9", 11 | "metadata": { 12 | "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." 13 | } 14 | }, 15 | "resourceGroupLocation": { 16 | "type": "string", 17 | "defaultValue": "westus", 18 | "metadata": { 19 | "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support." 20 | } 21 | }, 22 | "resourceName": { 23 | "type": "string", 24 | "defaultValue": "DurableFunctionsSampleTVA", 25 | "metadata": { 26 | "description": "Name of the main resource to be created by this template." 27 | } 28 | }, 29 | "resourceLocation": { 30 | "type": "string", 31 | "defaultValue": "[parameters('resourceGroupLocation')]", 32 | "metadata": { 33 | "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." 34 | } 35 | } 36 | }, 37 | "resources": [ 38 | { 39 | "type": "Microsoft.Resources/resourceGroups", 40 | "name": "[parameters('resourceGroupName')]", 41 | "location": "[parameters('resourceGroupLocation')]", 42 | "apiVersion": "2019-10-01" 43 | }, 44 | { 45 | "type": "Microsoft.Resources/deployments", 46 | "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", 47 | "resourceGroup": "[parameters('resourceGroupName')]", 48 | "apiVersion": "2019-10-01", 49 | "dependsOn": [ 50 | "[parameters('resourceGroupName')]" 51 | ], 52 | "properties": { 53 | "mode": "Incremental", 54 | "expressionEvaluationOptions": { 55 | "scope": "inner" 56 | }, 57 | "parameters": { 58 | "resourceGroupName": { 59 | "value": "[parameters('resourceGroupName')]" 60 | }, 61 | "resourceGroupLocation": { 62 | "value": "[parameters('resourceGroupLocation')]" 63 | }, 64 | "resourceName": { 65 | "value": "[parameters('resourceName')]" 66 | }, 67 | "resourceLocation": { 68 | "value": "[parameters('resourceLocation')]" 69 | } 70 | }, 71 | "template": { 72 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 73 | "contentVersion": "1.0.0.0", 74 | "parameters": { 75 | "resourceGroupName": { 76 | "type": "string" 77 | }, 78 | "resourceGroupLocation": { 79 | "type": "string" 80 | }, 81 | "resourceName": { 82 | "type": "string" 83 | }, 84 | "resourceLocation": { 85 | "type": "string" 86 | } 87 | }, 88 | "variables": { 89 | "storage_name": "[toLower(concat('storage', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId))))]", 90 | "storage_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Storage/storageAccounts/', variables('storage_name'))]", 91 | "function_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/sites/', parameters('resourceName'))]" 92 | }, 93 | "resources": [ 94 | { 95 | "location": "[parameters('resourceGroupLocation')]", 96 | "name": "[variables('storage_name')]", 97 | "type": "Microsoft.Storage/storageAccounts", 98 | "apiVersion": "2017-10-01", 99 | "tags": { 100 | "[concat('hidden-related:', concat('/providers/Microsoft.Web/sites/', parameters('resourceName')))]": "empty" 101 | }, 102 | "properties": { 103 | "supportsHttpsTrafficOnly": true 104 | }, 105 | "sku": { 106 | "name": "Standard_LRS" 107 | }, 108 | "kind": "Storage" 109 | }, 110 | { 111 | "location": "[parameters('resourceLocation')]", 112 | "name": "[parameters('resourceName')]", 113 | "type": "Microsoft.Web/sites", 114 | "apiVersion": "2015-08-01", 115 | "dependsOn": [ 116 | "[variables('storage_ResourceId')]" 117 | ], 118 | "kind": "functionapp", 119 | "properties": { 120 | "name": "[parameters('resourceName')]", 121 | "kind": "functionapp", 122 | "httpsOnly": true, 123 | "reserved": false 124 | }, 125 | "identity": { 126 | "type": "SystemAssigned" 127 | }, 128 | "resources": [ 129 | { 130 | "name": "appsettings", 131 | "type": "config", 132 | "apiVersion": "2015-08-01", 133 | "dependsOn": [ 134 | "[variables('function_ResourceId')]" 135 | ], 136 | "properties": { 137 | "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]", 138 | "WEBSITE_CONTENTSHARE": "[toLower(parameters('resourceName'))]", 139 | "AzureWebJobsDashboard": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]", 140 | "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]", 141 | "FUNCTIONS_EXTENSION_VERSION": "~3", 142 | "FUNCTIONS_WORKER_RUNTIME": "dotnet" 143 | } 144 | } 145 | ] 146 | } 147 | ] 148 | } 149 | } 150 | } 151 | ] 152 | } --------------------------------------------------------------------------------