{"id":23100912,"url":"https://github.com/sergiobarriel/turing-challenge","last_synced_at":"2026-03-20T00:35:45.307Z","repository":{"id":259952068,"uuid":"878501294","full_name":"sergiobarriel/turing-challenge","owner":"sergiobarriel","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-28T19:45:18.000Z","size":416,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-03T21:17:23.173Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sergiobarriel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-25T14:05:19.000Z","updated_at":"2024-11-01T19:20:55.000Z","dependencies_parsed_at":"2024-10-28T19:48:47.192Z","dependency_job_id":"bb84b602-002e-4bbb-aea4-104abee46039","html_url":"https://github.com/sergiobarriel/turing-challenge","commit_stats":null,"previous_names":["sergiobarriel/turing-challenge"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sergiobarriel/turing-challenge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2Fturing-challenge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2Fturing-challenge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2Fturing-challenge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2Fturing-challenge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sergiobarriel","download_url":"https://codeload.github.com/sergiobarriel/turing-challenge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiobarriel%2Fturing-challenge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28336519,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-16T23:48:57.922Z","updated_at":"2026-01-12T07:28:40.313Z","avatar_url":"https://github.com/sergiobarriel.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Turing Challenge\r\n\r\n## Top-level diagram\r\n\r\n![top level diagram](images/top-level-diagram.png)\r\n\r\n### 1. Authentication / Authorization\r\n\r\nThe client registers in **Azure API Management** developer portal and then makes a request to the Azure API Management API endpoint using their **subscription key**. Azure API Management validates the subscription key along with applicable quotas (policies) and forwards the request to the Function App. Before performing any action, the Function App can validate which **products** the subscriber has access to.\r\n\r\nRequest example:\r\n\r\n```\r\nGET https://turing-challenge.azure-api.net/api/endpoint?address=xxx\r\nOcp-Apim-Subscription-Key: \u003csubscription_key\u003e\r\n```\r\nAnother valid approach could focus on using **Entra ID / Azure AD** for managing authentication and authorization, as well as implementing middleware in the application to control aspects such as quotas, caching, and more.\n\r\n### 2. Function App\r\n\r\nThe Function App executes a Durable Function using the Function [**Function chaining pattern**](https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview?tabs=isolated-process%2Cnodejs-v3%2Cv1-model\u0026pivots=csharp#chaining). Each activity performs necessary tasks within its own scope, such as calling third-party services, storing or reading data in a database, or saving files. Each Function also logs telemetry and custom metrics.\r\n\r\n![top level diagram](images/function-chaining.png)\r\n\r\n#### Considerations\r\n\r\nThe Function App could be *Consuption plan* because it supports\r\n- 1.000.000 monthly request for free\r\n- Up to 200 instances\r\n- Up to 5 minutes timeout\r\n\r\nAzure Function example:\r\n\r\n```csharp\r\n[FunctionName(\"Chaining\")]\r\npublic static async Task\u003cobject\u003e RunAsync(\r\n    [OrchestrationTrigger] IDurableOrchestrationContext context)\r\n{\r\n    try\r\n    {\r\n        var x = await context.CallActivityAsync\u003cobject\u003e(\"F1\", null);\r\n        var y = await context.CallActivityAsync\u003cobject\u003e(\"F2\", x);\r\n        var z = await context.CallActivityAsync\u003cobject\u003e(\"F3\", y);\r\n        return  await context.CallActivityAsync\u003cobject\u003e(\"F4\", z);\r\n    }\r\n    catch (Exception)\r\n    {\r\n        // Error handling or compensation goes here.\r\n    }\r\n}\r\n```\r\n\r\nResponse example:\r\n\r\n```json\r\n{\r\n    \"propertyOne\": \"\",\r\n    \"propertyTwo\": \"\",\r\n    \"imageSasUri\": \"https://accountName.blob.core.windows.net/xxx.png?sv=xxx\u0026se=xxx\u0026sr=xxx\u0026sig=xxx\"\r\n}\r\n```\n\n*Note: The file is a reference with a SAS token, never a byte array*\n\r\n### 3. Telemetry\n\nBetween each call to an activity, we can log **custom metrics** in Application Insights to capture timing data. We can then retrieve this timing information using Kusto queries (KQL) and generate alerts when values fall outside specified thresholds.\r\n\nAzure Function example:\n\r\n```csharp\r\n [FunctionName(\"Chaining\")]\r\n    public static async Task\u003cobject\u003e RunAsync(\r\n        [OrchestrationTrigger] IDurableOrchestrationContext context)\r\n    {\r\n        var telemetryClient = new TelemetryClient();\r\n\r\n        try\r\n        {\r\n            // Measure time for F1\r\n            var start = DateTime.UtcNow;\r\n            var x = await context.CallActivityAsync\u003cobject\u003e(\"F1\", null);\r\n            var elapsed = DateTime.UtcNow - start;\r\n            telemetryClient.TrackMetric(\"F1Duration\", elapsed.TotalMilliseconds);\r\n\r\n            // Measure time for F2\r\n            start = DateTime.UtcNow;\r\n            var y = await context.CallActivityAsync\u003cobject\u003e(\"F2\", x);\r\n            elapsed = DateTime.UtcNow - start;\r\n            telemetryClient.TrackMetric(\"F2Duration\", elapsed.TotalMilliseconds);\r\n\r\n            // Measure time for F3\r\n            start = DateTime.UtcNow;\r\n            var z = await context.CallActivityAsync\u003cobject\u003e(\"F3\", y);\r\n            elapsed = DateTime.UtcNow - start;\r\n            telemetryClient.TrackMetric(\"F3Duration\", elapsed.TotalMilliseconds);\r\n\r\n            // Measure time for F4\r\n            start = DateTime.UtcNow;\r\n            var result = await context.CallActivityAsync\u003cobject\u003e(\"F4\", z);\r\n            elapsed = DateTime.UtcNow - start;\r\n            telemetryClient.TrackMetric(\"F4Duration\", elapsed.TotalMilliseconds);\r\n\r\n            return result;\r\n        }\r\n        catch (Exception ex)\r\n        {\r\n            telemetryClient.TrackException(ex);\r\n            throw;\r\n        }\r\n    }\r\n```\r\n\r\n### 4. Health checks \u0026 Azure Monitor\r\n\r\nBy include Health checks API in Function App we can monitor availability and performance even third-party integrations.\r\n\r\nExample request:\r\n\r\n```\r\nGET https://turing-challenge.azure-api.net/api/health-checks\r\nOcp-Apim-Subscription-Key: \u003csubscription_key\u003e\r\n```\r\n\nImplementation example:\n\n```csharp\n\npublic class GoogleMapsHealthCheck : IHealthCheck\n{\n    private readonly HttpClient _httpClient;\n\n    public GoogleMapsHealthCheck()\n    {\n        _httpClient = new HttpClient();\n    }\n\n    public async Task\u003cHealthCheckResult\u003e CheckAsync(HealthCheckContext context, CancellationToken cancellationToken = default)\n    {\n        var apiKey = Environment.GetEnvironmentVariable(\"GOOGLE_MAPS_API_KEY\");\n        var response = await _httpClient.GetAsync($\"https://maps.googleapis.com/maps/api/geocode/json?address=xxx\u0026key={apiKey}\");\n\n        if (response.IsSuccessStatusCode)\n        {\n            return HealthCheckResult.Healthy(\"Google Maps API is reachable.\");\n        }\n\n        return HealthCheckResult.Unhealthy(\"Google Maps API is not reachable.\");\n    }\n}\n```\n\r\nExample response:\r\n\r\n```json\r\n{\r\n  \"status\": \"Healthy\",\r\n  \"details\": {\r\n    \"AzureCosmosDB\": {\r\n      \"status\": \"Healthy\",\r\n      \"responseTime\": \"120ms\",\r\n      \"details\": {\r\n        \"database\": \"connected\",\r\n        \"lastCheck\": \"2024-10-28T10:30:00Z\"\r\n      }\r\n    },\r\n    \"AzureStorage\": {\r\n      \"status\": \"Degraded\",\r\n      \"responseTime\": \"300ms\",\r\n      \"details\": {\r\n        \"storageAccount\": \"connected\",\r\n        \"lastCheck\": \"2024-10-28T10:30:00Z\",\r\n        \"issues\": \"High response time\"\r\n      }\r\n    },\r\n    \"GoogleMapsAPI\": {\r\n      \"status\": \"Unhealthy\",\r\n      \"responseTime\": \"500ms\",\r\n      \"details\": {\r\n        \"error\": \"503 Service Unavailable\",\r\n        \"lastCheck\": \"2024-10-28T10:30:00Z\",\r\n        \"message\": \"Google Maps API is not reachable.\"\r\n      }\r\n    }\r\n  },\r\n  \"timestamp\": \"2024-10-28T10:30:05Z\"\r\n}\r\n```\r\n\r\nNow, with the response we have configured using the Health Checks API, we can connect it to the \"availability\" module in Azure Monitor and configure alerts in case a service malfunctions\r\n\r\n![top level diagram](images/azure-monitor-standard-test.png)\r\n\r\nWith an alert, we can trigger another Azure Function or Logic App (for example) to create a Jira ticket.\r\n\r\n### 5. CI/CD\r\n\r\nTo implement a CI/CD pipeline in Azure DevOps that triggers on Pull Requests, you can create a YAML file that defines the necessary steps to build the solution, run xUnit tests, and deploy an Azure Function App. Below is a detailed example of how to set this up.\r\n\r\n![top level diagram](images/cicd.png)\r\n\r\nThis a more detailed diagram about Gitflow:\r\n\r\n![top level diagram](images/gitflow.png)\n\nYML example:\n\n```yml\ntrigger: \n  branches:\n    exclude:\n      - '*'\n\npr:\n  branches:\n    include:\n      - main\n\njobs:\n- job: buildanddeploy\n  displayName: 'build, test, and deploy'\n  pool:\n    vmImage: 'windows-latest'\n\n  steps:\n  - task: UseDotNet@2\n    inputs:\n      packageType: 'sdk'\n      version: '6.x'\n      installationPath: $(Agent.ToolsDirectory)/dotnet\n\n  - script: |\n      dotnet restore\n    displayName: 'restore dependencies'\n\n  - script: |\n      dotnet build --configuration Release\n    displayName: 'build solution'\n\n  - script: |\n      dotnet test --configuration Release --no-build --verbosity normal\n    displayName: 'run xunit tests'\n\n  - task: AzureFunctionApp@1\n    inputs:\n      azureSubscription: 'YOUR_AZURE_SUBSCRIPTION'\n      appType: 'functionApp'\n      appName: '\u003cYOUR_FUNCTION_APP_NAME\u003e'\n      package: '$(System.DefaultWorkingDirectory)/**/*.zip'\n      deploymentMethod: 'zipDeploy'\n```\r\n\r\n### 7. Costs\r\n\r\nMonthly costs will be around 150€.\r\n\r\n| Resource | Tier | Monthly cost |\r\n|--|--|--|\r\n| Azure Function App | Consumption | 0€ |\r\n| Azure Storage Account | Writes up to 200k files and store 200Gb | 5€ |\r\n| Azure API Management | Basic | 130€ |\r\n| Azure Cosmos Db | Serverless | 30€  |\r\n\r\nCosts can be optimized with a few considerations:\r\n\r\n- We can create budgets in Azure Cost Management and alerts to notify subscription administrators\r\n- Older files can be moved to cold storage\r\n- Minimize the telemetry data we store\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiobarriel%2Fturing-challenge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsergiobarriel%2Fturing-challenge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiobarriel%2Fturing-challenge/lists"}