From 22bcffd9210cb4b8a64ae5e9ad9e05fd5c284475 Mon Sep 17 00:00:00 2001 From: Casper Thygesen Date: Fri, 3 Nov 2023 08:52:29 +0000 Subject: [PATCH] init --- .devcontainer/Dockerfile | 23 + .devcontainer/devcontainer.json | 67 +++ .env | 1 + .gitattributes | 65 +++ .github/workflows/e2e.yaml | 107 +++++ .github/workflows/wait-for-all-checks.yml | 13 + .github/workflows/webapi-docker-image.yml | 35 ++ .github/workflows/webapi-swagger.yaml | 31 ++ .gitignore | 411 ++++++++++++++++++ .vscode/launch.json | 35 ++ .vscode/tasks.json | 41 ++ README.md | 93 ++++ docs/index.md | 16 + mkdocs.yaml | 13 + renovate.json5 | 6 + src/.config/dotnet-tools.json | 12 + src/.dockerignore | 26 ++ src/.env | 2 + .../Cheetah.WebApi.Test.csproj | 27 ++ src/Cheetah.WebApi.sln | 40 ++ src/Cheetah.WebApi/.dockerignore | 26 ++ src/Cheetah.WebApi/AssemblyAnchor.cs | 6 + src/Cheetah.WebApi/Cheetah.WebApi.csproj | 36 ++ src/Cheetah.WebApi/Cheetah.WebApi.xml | 26 ++ .../Core/Config/KafkaConsumerConfig.cs | 11 + .../Core/Config/KafkaProducerConfig.cs | 11 + .../Core/Config/OpenSearchConfig.cs | 11 + src/Cheetah.WebApi/Core/Config/Priorities.cs | 14 + .../Core/Config/PrometheusConfig.cs | 8 + .../Core/Config/WebApiConfig.cs | 14 + .../Core/Models/DateAndValue.cs | 9 + src/Cheetah.WebApi/Dockerfile | 38 ++ .../HostedServices/TopicSubscriberService.cs | 34 ++ .../Installers/FluentValidationInstaller.cs | 24 + .../Installers/HealthCheckInstaller.cs | 19 + .../Installers/KafkaConsumerInstaller.cs | 56 +++ .../Installers/KafkaProducerInstaller.cs | 52 +++ .../Installers/OpenApiInstaller.cs | 51 +++ .../Installers/ServiceInstaller.cs | 48 ++ .../Controllers/DefaultController.cs | 13 + .../Controllers/ErrorController.cs | 30 ++ .../Controllers/KafkaController.cs | 63 +++ .../Controllers/OpenSearchController.cs | 37 ++ src/Cheetah.WebApi/Program.cs | 121 ++++++ .../Properties/launchSettings.json | 32 ++ src/Cheetah.WebApi/appsettings.json | 7 + src/Cheetah.WebApi/swagger.json | 142 ++++++ src/NuGet-CI.Config | 17 + src/docker-compose.yml | 57 +++ 49 files changed, 2077 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .env create mode 100644 .gitattributes create mode 100644 .github/workflows/e2e.yaml create mode 100644 .github/workflows/wait-for-all-checks.yml create mode 100644 .github/workflows/webapi-docker-image.yml create mode 100644 .github/workflows/webapi-swagger.yaml create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 README.md create mode 100644 docs/index.md create mode 100644 mkdocs.yaml create mode 100644 renovate.json5 create mode 100644 src/.config/dotnet-tools.json create mode 100644 src/.dockerignore create mode 100644 src/.env create mode 100644 src/Cheetah.WebApi.Test/Cheetah.WebApi.Test.csproj create mode 100644 src/Cheetah.WebApi.sln create mode 100644 src/Cheetah.WebApi/.dockerignore create mode 100644 src/Cheetah.WebApi/AssemblyAnchor.cs create mode 100644 src/Cheetah.WebApi/Cheetah.WebApi.csproj create mode 100644 src/Cheetah.WebApi/Cheetah.WebApi.xml create mode 100644 src/Cheetah.WebApi/Core/Config/KafkaConsumerConfig.cs create mode 100644 src/Cheetah.WebApi/Core/Config/KafkaProducerConfig.cs create mode 100644 src/Cheetah.WebApi/Core/Config/OpenSearchConfig.cs create mode 100644 src/Cheetah.WebApi/Core/Config/Priorities.cs create mode 100644 src/Cheetah.WebApi/Core/Config/PrometheusConfig.cs create mode 100644 src/Cheetah.WebApi/Core/Config/WebApiConfig.cs create mode 100644 src/Cheetah.WebApi/Core/Models/DateAndValue.cs create mode 100644 src/Cheetah.WebApi/Dockerfile create mode 100644 src/Cheetah.WebApi/HostedServices/TopicSubscriberService.cs create mode 100644 src/Cheetah.WebApi/Infrastructure/Installers/FluentValidationInstaller.cs create mode 100644 src/Cheetah.WebApi/Infrastructure/Installers/HealthCheckInstaller.cs create mode 100644 src/Cheetah.WebApi/Infrastructure/Installers/KafkaConsumerInstaller.cs create mode 100644 src/Cheetah.WebApi/Infrastructure/Installers/KafkaProducerInstaller.cs create mode 100644 src/Cheetah.WebApi/Infrastructure/Installers/OpenApiInstaller.cs create mode 100644 src/Cheetah.WebApi/Infrastructure/Installers/ServiceInstaller.cs create mode 100644 src/Cheetah.WebApi/Presentation/Controllers/DefaultController.cs create mode 100644 src/Cheetah.WebApi/Presentation/Controllers/ErrorController.cs create mode 100644 src/Cheetah.WebApi/Presentation/Controllers/KafkaController.cs create mode 100644 src/Cheetah.WebApi/Presentation/Controllers/OpenSearchController.cs create mode 100644 src/Cheetah.WebApi/Program.cs create mode 100644 src/Cheetah.WebApi/Properties/launchSettings.json create mode 100644 src/Cheetah.WebApi/appsettings.json create mode 100644 src/Cheetah.WebApi/swagger.json create mode 100644 src/NuGet-CI.Config create mode 100644 src/docker-compose.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..428bb19 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,23 @@ +# [Choice] .NET version: 6.0-bullseye-slim, 6.0-jammy, 6.0-focal +FROM mcr.microsoft.com/devcontainers/dotnet:0-6.0-jammy + +ENV DOCKER_BUILDKIT=1 +ENV DOCKER_DEFAULT_PLATFORM=linux/amd64 + +# [Optional] Uncomment this section to install additional OS packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends git curl exa + +# Install Docker to use in docker-in-docker +RUN apt-get update && apt-get install -y curl \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh +# Add user "admin" to the Docker group +#&& usermod -a -G docker admin +ADD https://raw.githubusercontent.com/docker/docker-ce/master/components/cli/contrib/completion/bash/docker /etc/bash_completion.d/docker.sh + +USER vscode +ARG GITHUB_ACTOR +ARG GITHUB_TOKEN + +RUN dotnet nuget add source https://nuget.pkg.github.com/trifork/index.json -n trifork-github -u $GITHUB_ACTOR -p $GITHUB_TOKEN --store-password-in-clear-text diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8c55f1f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,67 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet +{ + "name": ".NET DevContainer", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "build": { + "dockerfile": "./Dockerfile", + "context": ".", + "args": { + "GITHUB_ACTOR": "${localEnv:GITHUB_ACTOR}", + "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}" + } + }, + "containerEnv": { + "GITHUB_ACTOR": "${localEnv:GITHUB_ACTOR}", + "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}" + }, + "runArgs": [ + // Network where OpenSearch is running + "--network=cheetah-infrastructure" + ], + "initializeCommand": "docker network create cheetah-infrastructure || true", + "customizations": { + "vscode": { + "extensions": [ + // Recommended extensions - GitHub + "GitHub.vscode-pull-request-github", + "GitHub.copilot", + // Recommended extensions - Collaboration + "eamodio.gitlens", + "EditorConfig.EditorConfig", + "MS-vsliveshare.vsliveshare-pack", + "streetsidesoftware.code-spell-checker", + "redhat.vscode-yaml", + // Recommended extensions - .NET + "Fudge.auto-using", + "jongrant.csharpsortusings", + "kreativ-software.csharpextensions", + // Recommended extensions - Markdown + "bierner.github-markdown-preview", + "DavidAnson.vscode-markdownlint", + "docsmsft.docs-linting", + "yzhang.markdown-all-in-one", + // Required extensions + "ms-dotnettools.csharp", + "VisualStudioExptTeam.vscodeintellicode", + "aliasadidev.nugetpackagemanagergui" + ] + } + }, + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {} + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [5000, 5001], + // "portsAttributes": { + // "5001": { + // "protocol": "https" + // } + // } + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "dotnet restore", + // Configure tool-specific properties. + // "customizations": {}, + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + "remoteUser": "root" +} \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..22d5500 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +COMPOSE_FILE=src/docker-compose.yml \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0401918 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,65 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto +*.cs text eol=crlf +*.sh text eol=lf + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 0000000..dadd513 --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,107 @@ +name: E2E + +on: + workflow_call: + workflow_dispatch: + schedule: + - cron: "0 3 * * *" + push: + branches: [ 'main', 'release/v**' ] + pull_request: + branches: [ 'main', 'release/v**' ] + types: [ opened, synchronize, reopened, labeled ] + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +env: + CONTEXT: . + +jobs: + should-run: # seperate step to support ci/cd status checks + runs-on: ubuntu-latest + outputs: + tag-check: ${{ steps.tag-check.outputs.value }} + event-name-check: ${{ steps.event-name-check.outputs.value }} + + steps: + # the pull request contains the 'e2e-test' label + - name: Tag check + id: tag-check + if: ${{ contains(github.event.pull_request.labels.*.name, 'e2e-test') && !contains(github.event.pull_request.labels.*.name, 'blocked') }} + run: echo "value=true" >> "$GITHUB_OUTPUT" + + # not started from a pull-request + - name: Check for specific label + id: event-name-check + if: ${{github.event_name != 'pull_request'}} + run: echo "value=true" >> "$GITHUB_OUTPUT" + +should-run: + uses: trifork/cheetah-infrastructure-utils-workflows/.github/workflows/e2e-should-run.yml@main + + e2e-test: + needs: should-run + if: ${{ needs.should-run.outputs.should-run }} + runs-on: ubuntu-latest + if: ${{ needs.should-run.outputs.event-name-check == 'true' || needs.should-run.outputs.tag-check == 'true' }} + steps: + - name: Checkout repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 + + - name: Log in to the Container registry + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.PACKAGE_PAT }} + + - name: Checkout trifork/cheetah-development-infrastructure + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 + with: + repository: trifork/cheetah-development-infrastructure + token: ${{ secrets.PACKAGE_PAT }} # `PACKAGE_PAT` is a secret that contains your PAT + path: integrationtests + + - name: "Start e2e infrastructure" + working-directory: integrationtests/ + run: | + docker compose --profile core up -d --build + env: + DOCKER_REGISTRY: ghcr.io/trifork/ + + - name: "Wait for opensearch, the slowest component" + uses: nick-fields/retry@v2 + with: + timeout_minutes: 3 + max_attempts: 25 + retry_wait_seconds: 5 + warning_on_retry: false + command: 'docker run --rm --network=cheetah-infrastructure badouralix/curl-jq curl -u admin:admin -sS -X GET -H "Content-Type: application/json" http://opensearch:9200/_cat/indices' + + - name: "Start Cheetah.Webapi (example)" + working-directory: . + run: docker compose up -d --build + env: + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_TOKEN: ${{ secrets.PACKAGE_PAT }} + + - name: "Check health" + id: "integrationtest" + uses: nick-fields/retry@v2 + with: + timeout_minutes: 1 + max_attempts: 25 + retry_wait_seconds: 5 + warning_on_retry: false + command: 'docker run --rm --network=cheetah-infrastructure badouralix/curl-jq curl -sS -X GET -H "Content-Type: application/json" http://cheetahwebapi:80/health' + + - name: "Check metrics" + shell: bash + run: | + docker run --rm --network=cheetah-infrastructure badouralix/curl-jq curl -sS -X GET -H "Content-Type: application/json" http://cheetahwebapi:80/metrics + + - name: "Print logs" + if: always() + run: "docker logs src-cheetah.webapi-1" # docker compose logs diff --git a/.github/workflows/wait-for-all-checks.yml b/.github/workflows/wait-for-all-checks.yml new file mode 100644 index 0000000..70978c9 --- /dev/null +++ b/.github/workflows/wait-for-all-checks.yml @@ -0,0 +1,13 @@ +name: summary +on: + pull_request: +jobs: + enforce-all-checks: + runs-on: ubuntu-latest + permissions: + checks: read + steps: + - name: GitHub Checks + uses: poseidon/wait-for-status-checks@v0.3.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/webapi-docker-image.yml b/.github/workflows/webapi-docker-image.yml new file mode 100644 index 0000000..d1451ca --- /dev/null +++ b/.github/workflows/webapi-docker-image.yml @@ -0,0 +1,35 @@ +name: Create and publish Cheetah.WebApi Docker image + +on: + workflow_dispatch: + + push: + branches: ["main"] + tags: + - "v*" + pull_request: + branches: ["main"] + +env: + IMAGE_NAME: trifork/cheetah-webapi # image name, must have gh org prefix + ASSEMBLY_NAME: Cheetah.WebApi.dll # entrypoint assembly name + PROJECT_PATH: Cheetah.WebApi/Cheetah.WebApi.csproj # path to csproj, relative to ./src (i know..) + BASE_IMAGE: src/Cheetah.WebApi/Dockerfile + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 + - name: "Build and push" + uses: trifork/cheetah-infrastructure-utils-workflows/.github/actions/build-image@main + with: + read_package_pat: ${{ secrets.PACKAGE_PAT }} # we need this, as GITHUB_TOKEN only have permission to its own repo + image_name: ${{ env.IMAGE_NAME }} + project_path: ${{ env.PROJECT_PATH }} + assembly_name: ${{ env.ASSEMBLY_NAME }} + github_actor: ${{ github.actor }} + github_token: ${{ secrets.GITHUB_TOKEN }} + base_image: ${{ env.BASE_IMAGE }} + github_run_id: ${{ github.run_id }} diff --git a/.github/workflows/webapi-swagger.yaml b/.github/workflows/webapi-swagger.yaml new file mode 100644 index 0000000..f1568a0 --- /dev/null +++ b/.github/workflows/webapi-swagger.yaml @@ -0,0 +1,31 @@ +name: Create and publish Cheetah.WebApi swagger files + +on: + workflow_dispatch: + + push: + branches: ["main"] + tags: + - "v*" + pull_request: + branches: ["main"] + +env: + ASSEMBLY_NAME: Cheetah.WebApi.dll # entrypoint assembly name + PROJECT_PATH: src/Cheetah.WebApi + SOLUTION_FILEPATH: src/Cheetah.WebApi.sln + +jobs: + generate-swagger: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 + - name: "Generate swagger file" + uses: trifork/cheetah-infrastructure-utils/.github/actions/generate-swagger@main + with: + read_package_pat: ${{ secrets.PACKAGE_PAT }} # we need this, as GITHUB_TOKEN only have permission to its own repo + project_path: ${{ env.PROJECT_PATH }} + assembly_name: ${{ env.ASSEMBLY_NAME }} + github_actor: ${{ github.actor }} + solution_filepath: ${{ env.SOLUTION_FILEPATH }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2dd7e78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,411 @@ +### Custom + +*.pfx +src/ASP.NET/ + + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +**/bin +**/obj +**/.vs +**/.vs +.idea +bin/ +obj/ +.nuget/ +src/rest-example-app/Properties/launchSettings.json +src/cheetah-web.sln.DotSettings.user +.DS_Store diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9117c56 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/src/Cheetah.WebApi/bin/Debug/net6.0/Cheetah.WebApi.dll", + "args": [], + "cwd": "${workspaceFolder}/src/Cheetah.WebApi", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..eec317b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/Cheetah.WebApi.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/src/Cheetah.WebApi.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/src/Cheetah.WebApi.sln" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ae6d6c --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# Cheetah.WebApi + +This project contains the demo version of a Cheetah.WebApi. +The project utilizes DockerFiles and docker-compose to compartmentalize instances of the project and to help with ease of deployment. +It is assumed that the user of This template has an understanding of Docker and docker-compose, as well as an familiarity with an terminal of they choice. + +## Running the application + +Please setup a local certificate: + +```shell +dotnet dev-certs https -ep "$env:APPDATA/ASP.NET/https/aspnetapp.pfx" -p "password" +dotnet dev-certs https -t # trust +``` + +### Authentication + +For PoC purpose, we are using a `OAuthSimulator`. +It can be accessed at https://localhost:1852/swagger/index.html + + +## Docker prerequisites + +Ensure that you have the latest version of Visual Studio and [Docker Desktop](https://www.docker.com/products/docker-desktop) installed. +You may need to enable virtualization in your BIOS. +_NB:_ We are using Linux containers. + +## Dependencies + +Currently the project uses a nuget package from Trifork's repository Called Cheetah.Shared-{versionNumber} + +To source this dependency you will first need to create a Personal Access Token to to a Github-account with access to this repository. + + + +### Option A) Configure nuget source inside Visual studio + +1. Navigate to **Tools**->**Options** +2. Find the **NuGet Package Manager** -> **Package Sources** +3. Add following source: "https://nuget.pkg.github.com/trifork/index.json" +4. click OK +5. Restart Visual Studio, if it's open. +6. Open NuGet Pack Manager and select the new source. You will now be prompted for a username and password. Enter your account-name into username. And use your PAT as your password + +### Option B) Configure NuGet config directly + +Add the source in your private `$HOME/.nuget/NuGet/NuGet.Config` file, e.g. + +```xml + + + + + + + + + + + + + + + + + +``` + +### Running Docker outside Visual Studio + +The project can be run with `docker-compose` outside Visual Studio with a few prerequisites. +This can be useful when you are not interested in debugging the service and want to use less computer resources. + +Supply NuGet credentials through environment variables: + +```powershell +[System.Environment]::SetEnvironmentVariable('GITHUB_ACTOR', '', [System.EnvironmentVariableTarget]::User) +[System.Environment]::SetEnvironmentVariable('GITHUB_TOKEN', '', [System.EnvironmentVariableTarget]::User) +``` + +Commands: + +```shell +# Start container using docker-compose.yml +docker-compose up +# OR start container using docker-compose.yml as detached (will run in background) +docker-compose up -d +``` + +#### Sources + +[Docker basics](https://docs.docker.com/get-started/) +[Docker-compose](https://docs.docker.com/compose/) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..22b868c --- /dev/null +++ b/docs/index.md @@ -0,0 +1,16 @@ +# Introduction + +This project contains the demo version of a Cheetah.WebApi. +Checkout repository locally to read readme.me about how to develop on the service + +## Purpose & Responsibilty + +Showcasing + +## How to debug + +Standard observability is used. + +## FAQ + +TBD \ No newline at end of file diff --git a/mkdocs.yaml b/mkdocs.yaml new file mode 100644 index 0000000..a5f7f52 --- /dev/null +++ b/mkdocs.yaml @@ -0,0 +1,13 @@ +site_name: "Cheetah.WebApi" + +nav: + - Home: index.md + +plugins: + - techdocs-core + - kroki: + ServerURL: https://kroki.cheetah.trifork.dev + EnableMermaid: false + EnableBlockDiag: false + Enablebpmn: false + EnableExcalidraw: false diff --git a/renovate.json5 b/renovate.json5 new file mode 100644 index 0000000..6640f81 --- /dev/null +++ b/renovate.json5 @@ -0,0 +1,6 @@ +{ + "extends": [ + "github>trifork/cheetah-infrastructure-utils:default.json5" + ], + "dependencyDashboardApproval": true +} diff --git a/src/.config/dotnet-tools.json b/src/.config/dotnet-tools.json new file mode 100644 index 0000000..1a1607f --- /dev/null +++ b/src/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "swashbuckle.aspnetcore.cli": { + "version": "6.2.3", + "commands": [ + "swagger" + ] + } + } +} \ No newline at end of file diff --git a/src/.dockerignore b/src/.dockerignore new file mode 100644 index 0000000..d16e1a4 --- /dev/null +++ b/src/.dockerignore @@ -0,0 +1,26 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +**/.nuget +LICENSE +README.md diff --git a/src/.env b/src/.env new file mode 100644 index 0000000..0bfde57 --- /dev/null +++ b/src/.env @@ -0,0 +1,2 @@ +PROMETHEUS_KESTREL_CONTAINERPORT=1957 +PROMETHEUS_KESTREL_HOSTPORT=1957 diff --git a/src/Cheetah.WebApi.Test/Cheetah.WebApi.Test.csproj b/src/Cheetah.WebApi.Test/Cheetah.WebApi.Test.csproj new file mode 100644 index 0000000..89f55d1 --- /dev/null +++ b/src/Cheetah.WebApi.Test/Cheetah.WebApi.Test.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/Cheetah.WebApi.sln b/src/Cheetah.WebApi.sln new file mode 100644 index 0000000..40ef92b --- /dev/null +++ b/src/Cheetah.WebApi.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32210.238 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{43C40C63-9AC2-407E-9FE3-7A2F0FD19E59}" + ProjectSection(SolutionItems) = preProject + NuGet-CI.Config = NuGet-CI.Config + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cheetah.WebApi", "Cheetah.WebApi\Cheetah.WebApi.csproj", "{165D1AD9-4514-40C1-9A9E-BDC9F5167A2A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cheetah.WebApi.Test", "Cheetah.WebApi.Test\Cheetah.WebApi.Test.csproj", "{38E90DB2-57A8-4C75-AED7-DFA0FA623F99}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F27FB5B2-6652-46E4-BA8C-2D5B6CD24D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F27FB5B2-6652-46E4-BA8C-2D5B6CD24D15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F27FB5B2-6652-46E4-BA8C-2D5B6CD24D15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F27FB5B2-6652-46E4-BA8C-2D5B6CD24D15}.Release|Any CPU.Build.0 = Release|Any CPU + {165D1AD9-4514-40C1-9A9E-BDC9F5167A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {165D1AD9-4514-40C1-9A9E-BDC9F5167A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {165D1AD9-4514-40C1-9A9E-BDC9F5167A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {165D1AD9-4514-40C1-9A9E-BDC9F5167A2A}.Release|Any CPU.Build.0 = Release|Any CPU + {38E90DB2-57A8-4C75-AED7-DFA0FA623F99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38E90DB2-57A8-4C75-AED7-DFA0FA623F99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38E90DB2-57A8-4C75-AED7-DFA0FA623F99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38E90DB2-57A8-4C75-AED7-DFA0FA623F99}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {08478CE7-B34C-428F-88CF-5F592196F552} + EndGlobalSection +EndGlobal diff --git a/src/Cheetah.WebApi/.dockerignore b/src/Cheetah.WebApi/.dockerignore new file mode 100644 index 0000000..20da0a0 --- /dev/null +++ b/src/Cheetah.WebApi/.dockerignore @@ -0,0 +1,26 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +**/.nuget +LICENSE +README.md \ No newline at end of file diff --git a/src/Cheetah.WebApi/AssemblyAnchor.cs b/src/Cheetah.WebApi/AssemblyAnchor.cs new file mode 100644 index 0000000..66e35b7 --- /dev/null +++ b/src/Cheetah.WebApi/AssemblyAnchor.cs @@ -0,0 +1,6 @@ +namespace Cheetah.WebApi +{ + internal class AssemblyAnchor + { + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Cheetah.WebApi.csproj b/src/Cheetah.WebApi/Cheetah.WebApi.csproj new file mode 100644 index 0000000..a83b6f4 --- /dev/null +++ b/src/Cheetah.WebApi/Cheetah.WebApi.csproj @@ -0,0 +1,36 @@ + + + + net6.0 + Linux + 10 + True + Cheetah.WebApi.xml + bb3fdc17-ede2-42c1-82cf-eae032a2caa5 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cheetah.WebApi/Cheetah.WebApi.xml b/src/Cheetah.WebApi/Cheetah.WebApi.xml new file mode 100644 index 0000000..0b2ec8f --- /dev/null +++ b/src/Cheetah.WebApi/Cheetah.WebApi.xml @@ -0,0 +1,26 @@ + + + + Cheetah.WebApi + + + + + Consume a message from kafka + + + + + + Produce a message to kafka + + + + + + Retrieves all indices on the corresponding OpenSearch instance + + + + + diff --git a/src/Cheetah.WebApi/Core/Config/KafkaConsumerConfig.cs b/src/Cheetah.WebApi/Core/Config/KafkaConsumerConfig.cs new file mode 100644 index 0000000..cedc2fd --- /dev/null +++ b/src/Cheetah.WebApi/Core/Config/KafkaConsumerConfig.cs @@ -0,0 +1,11 @@ +using System.Net; + +namespace Cheetah.WebApi.Core.Config +{ + public class KafkaConsumerConfig + { + public const string Position = nameof(KafkaConsumerConfig); + public string Topic { get; set; } = "InputTopic"; + public string ConsumerName { get; set; } = Dns.GetHostName(); + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Core/Config/KafkaProducerConfig.cs b/src/Cheetah.WebApi/Core/Config/KafkaProducerConfig.cs new file mode 100644 index 0000000..2e7754a --- /dev/null +++ b/src/Cheetah.WebApi/Core/Config/KafkaProducerConfig.cs @@ -0,0 +1,11 @@ +using System.Net; + +namespace Cheetah.WebApi.Core.Config +{ + public class KafkaProducerConfig + { + public const string Position = nameof(KafkaProducerConfig); + public string Topic { get; set; } = "OutputTopic"; + public string ProducerName { get; set; } = Dns.GetHostName(); + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Core/Config/OpenSearchConfig.cs b/src/Cheetah.WebApi/Core/Config/OpenSearchConfig.cs new file mode 100644 index 0000000..3ed2161 --- /dev/null +++ b/src/Cheetah.WebApi/Core/Config/OpenSearchConfig.cs @@ -0,0 +1,11 @@ +using Cheetah.Core.Config; + +namespace Cheetah.WebApi.Core.Config +{ + public class WebApiOpenSearchConfig : OpenSearchConfig + { + public int PaginationSize { get; set; } = 1000; + public int ScrollLifetimeSeconds { get; set; } = 60; + public int MaxBuckets { get; set; } = ushort.MaxValue; //[search.max_buckets] cluster level setting + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Core/Config/Priorities.cs b/src/Cheetah.WebApi/Core/Config/Priorities.cs new file mode 100644 index 0000000..b4ef86a --- /dev/null +++ b/src/Cheetah.WebApi/Core/Config/Priorities.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Cheetah.WebApi.Core.Config +{ + public class Priorities + { + public const int BeforeConfig = 0; + public const int Default = 1; + public const int AfterConfig = 2; + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Core/Config/PrometheusConfig.cs b/src/Cheetah.WebApi/Core/Config/PrometheusConfig.cs new file mode 100644 index 0000000..31a92c4 --- /dev/null +++ b/src/Cheetah.WebApi/Core/Config/PrometheusConfig.cs @@ -0,0 +1,8 @@ +namespace Cheetah.WebApi.Core.Config +{ + public class PrometheusConfig + { + public const string Position = "Prometheus"; + public int Port { get; set; } + } +} diff --git a/src/Cheetah.WebApi/Core/Config/WebApiConfig.cs b/src/Cheetah.WebApi/Core/Config/WebApiConfig.cs new file mode 100644 index 0000000..dcd099f --- /dev/null +++ b/src/Cheetah.WebApi/Core/Config/WebApiConfig.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; + +namespace Cheetah.WebApi.Core.Config +{ + public class WebApiConfig + { + public const string Position = nameof(WebApiConfig); + // Custom settings here + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Core/Models/DateAndValue.cs b/src/Cheetah.WebApi/Core/Models/DateAndValue.cs new file mode 100644 index 0000000..619e59a --- /dev/null +++ b/src/Cheetah.WebApi/Core/Models/DateAndValue.cs @@ -0,0 +1,9 @@ +using System; + +namespace Cheetah.WebApi.Core.Models; + +public class DateAndValue +{ + public DateTimeOffset Date { get; set; } + public T Value { get; set; } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Dockerfile b/src/Cheetah.WebApi/Dockerfile new file mode 100644 index 0000000..634281b --- /dev/null +++ b/src/Cheetah.WebApi/Dockerfile @@ -0,0 +1,38 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build + +ARG GITHUB_ACTOR +ARG GITHUB_TOKEN + +WORKDIR /src +COPY "src/NuGet-CI.Config" "NuGet.config" +COPY "src/.config/" ".config/" +COPY ["src/Cheetah.WebApi/Cheetah.WebApi.csproj", "Cheetah.WebApi/"] +RUN --mount=type=secret,id=GITHUB_TOKEN \ + GITHUB_TOKEN="$(cat /run/secrets/GITHUB_TOKEN)" \ + dotnet restore "Cheetah.WebApi/Cheetah.WebApi.csproj" +COPY src . +WORKDIR "/src/Cheetah.WebApi" +RUN dotnet build "Cheetah.WebApi.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Cheetah.WebApi.csproj" -c Release -o /app/publish + +FROM base AS final +# Create a new user with bash and a homedir +RUN useradd -r -u 1001 -m -s /bin/bash dotnetuser + +# Set the user for subsequent commands +USER 1001 + +ENV COMPlus_EnableDiagnostics=0 + +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT dotnet "Cheetah.WebApi.dll" \ No newline at end of file diff --git a/src/Cheetah.WebApi/HostedServices/TopicSubscriberService.cs b/src/Cheetah.WebApi/HostedServices/TopicSubscriberService.cs new file mode 100644 index 0000000..b051154 --- /dev/null +++ b/src/Cheetah.WebApi/HostedServices/TopicSubscriberService.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Cheetah.WebApi.Core.Config; +using Confluent.Kafka; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; + +namespace Cheetah.WebApi +{ + class TopicSubscriberService : IHostedService + { + private IConsumer _kafkaConsumer; + private IOptions _kafkaConsumerOptions; + + public TopicSubscriberService(IConsumer kafkaConsumer, + IOptions kafkaConsumerOptions) + { + _kafkaConsumer = kafkaConsumer; + _kafkaConsumerOptions = kafkaConsumerOptions; + } + public Task StartAsync(CancellationToken cancellationToken) + { + _kafkaConsumer.Subscribe(_kafkaConsumerOptions.Value.Topic); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _kafkaConsumer.Close(); + return Task.CompletedTask; + } + } +} diff --git a/src/Cheetah.WebApi/Infrastructure/Installers/FluentValidationInstaller.cs b/src/Cheetah.WebApi/Infrastructure/Installers/FluentValidationInstaller.cs new file mode 100644 index 0000000..3217ceb --- /dev/null +++ b/src/Cheetah.WebApi/Infrastructure/Installers/FluentValidationInstaller.cs @@ -0,0 +1,24 @@ +using FluentValidation.AspNetCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Cheetah.WebApi.Shared.Infrastructure.ServiceProvider; +using Cheetah.WebApi.Core.Config; + +namespace Cheetah.WebApi.Infrastructure.Installers +{ + + [InstallerPriority(Priorities.BeforeConfig)] + public class FluentValidationInstaller : IServiceCollectionInstaller + { + public void Install(IServiceCollection services, IHostEnvironment hostEnvironment) + { + services.AddFluentValidation(config => + { + config.AutomaticValidationEnabled = false; + config.RegisterValidatorsFromAssemblyContaining(); + }); + + } + } +} + diff --git a/src/Cheetah.WebApi/Infrastructure/Installers/HealthCheckInstaller.cs b/src/Cheetah.WebApi/Infrastructure/Installers/HealthCheckInstaller.cs new file mode 100644 index 0000000..a9d3c24 --- /dev/null +++ b/src/Cheetah.WebApi/Infrastructure/Installers/HealthCheckInstaller.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Prometheus; +using Cheetah.WebApi.Shared.Infrastructure.ServiceProvider; +using Cheetah.WebApi.Core.Config; + +namespace Cheetah.WebApi.Infrastructure.Installers +{ + [InstallerPriority(Priorities.AfterConfig)] + public class HealthCheckInstaller : IServiceCollectionInstaller + { + public void Install(IServiceCollection services, IHostEnvironment hostEnvironment) + { + services.AddHealthChecks() + .ForwardToPrometheus(); + } + } +} + diff --git a/src/Cheetah.WebApi/Infrastructure/Installers/KafkaConsumerInstaller.cs b/src/Cheetah.WebApi/Infrastructure/Installers/KafkaConsumerInstaller.cs new file mode 100644 index 0000000..02890a2 --- /dev/null +++ b/src/Cheetah.WebApi/Infrastructure/Installers/KafkaConsumerInstaller.cs @@ -0,0 +1,56 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Cheetah.WebApi.Core.Config; +using Cheetah.WebApi.Shared.Infrastructure.ServiceProvider; +using Confluent.Kafka; +using Cheetah.Core.Config; +using Microsoft.Extensions.Logging; +using Cheetah.Core.Infrastructure.Services.Kafka; + +namespace Cheetah.WebApi.Infrastructure.Installers +{ + [InstallerPriority(Priorities.AfterConfig)] + public class KafkaConsumerInstaller : IServiceCollectionInstaller + { + public void Install(IServiceCollection services, IHostEnvironment hostEnvironment) + { + services.AddSingleton(localProvider => + { + var kafkaConsumerOptions = localProvider.GetRequiredService>(); + var kafkaConfig = localProvider.GetRequiredService>(); + var logger = localProvider.GetRequiredService>(); + var applicationLifetime = localProvider.GetRequiredService(); + var clientConfig = new ClientConfig + { + BootstrapServers = kafkaConfig.Value.Url, + SaslMechanism = SaslMechanism.OAuthBearer, + SecurityProtocol = SecurityProtocol.SaslPlaintext, + }; + + var consumer = new ConsumerBuilder(new ConsumerConfig(clientConfig) + { + GroupId = kafkaConsumerOptions.Value.ConsumerName, + AutoOffsetReset = AutoOffsetReset.Latest, + EnableAutoCommit = true, + }) + .SetErrorHandler((consumer1, error) => + { + if (error.IsError) + { + logger.LogError("Kafka fatal error: {Reason}", error.Reason); + applicationLifetime.StopApplication(); + throw new KafkaException(error); + } + + logger.LogWarning("Kafka error: {Reason}", error.Reason); + }) + .AddCheetahOAuthentication(localProvider) + .Build(); + + + return consumer; + }); + } + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Infrastructure/Installers/KafkaProducerInstaller.cs b/src/Cheetah.WebApi/Infrastructure/Installers/KafkaProducerInstaller.cs new file mode 100644 index 0000000..8d9cb6f --- /dev/null +++ b/src/Cheetah.WebApi/Infrastructure/Installers/KafkaProducerInstaller.cs @@ -0,0 +1,52 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Cheetah.WebApi.Core.Config; +using Cheetah.WebApi.Shared.Infrastructure.ServiceProvider; +using Confluent.Kafka; +using Cheetah.Core.Config; +using Microsoft.Extensions.Logging; +using Cheetah.Core.Infrastructure.Services.Kafka; + +namespace Cheetah.WebApi.Infrastructure.Installers +{ + [InstallerPriority(Priorities.AfterConfig)] + public class KafkaProducerInstaller : IServiceCollectionInstaller + { + public void Install(IServiceCollection services, IHostEnvironment hostEnvironment) + { + services.AddSingleton(localProvider => + { + var kafkaProducerConfig = localProvider.GetRequiredService>(); + var kafkaConfig = localProvider.GetRequiredService>(); + var logger = localProvider.GetRequiredService>(); + var applicationLifetime = localProvider.GetRequiredService(); + var clientConfig = new ClientConfig + { + BootstrapServers = kafkaConfig.Value.Url, + SaslMechanism = SaslMechanism.OAuthBearer, + SecurityProtocol = SecurityProtocol.SaslPlaintext, + }; + + var consumer = new ProducerBuilder(new ProducerConfig(clientConfig) + { + }) + .SetErrorHandler((consumer1, error) => + { + if (error.IsError) + { + logger.LogError("Kafka fatal error: {Reason}", error.Reason); + applicationLifetime.StopApplication(); + throw new KafkaException(error); + } + + logger.LogWarning("Kafka error: {Reason}", error.Reason); + }) + .AddCheetahOAuthentication(localProvider) + .Build(); + return consumer; + }); + + } + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Infrastructure/Installers/OpenApiInstaller.cs b/src/Cheetah.WebApi/Infrastructure/Installers/OpenApiInstaller.cs new file mode 100644 index 0000000..8f58bfd --- /dev/null +++ b/src/Cheetah.WebApi/Infrastructure/Installers/OpenApiInstaller.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using System; +using System.IO; +using System.Reflection; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Swashbuckle.AspNetCore.SwaggerGen; +using Cheetah.WebApi.Shared.Infrastructure.ServiceProvider; +using Cheetah.WebApi.Core.Config; + +namespace Cheetah.WebApi.Infrastructure.Installers +{ + [InstallerPriority(Priorities.BeforeConfig)] + public class OpenApiInstaller : IServiceCollectionInstaller + { + public void Install(IServiceCollection services, IHostEnvironment hostEnvironment) + { + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + services + .AddControllers() + //.ConfigureApiBehaviorOptions(x => { x.SuppressMapClientErrors = true; }) + .AddJsonOptions(options => + options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); + + services.AddApiVersioning(options => + { + // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions" + options.ReportApiVersions = true; + }) + .AddVersionedApiExplorer( + options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + services.Configure(options => options.LowercaseUrls = true); + // services.AddTransient, ConfigureSwaggerOptions>(); + + services.AddSwaggerGen(c => + { + var xmlPath = Path.Combine(AppContext.BaseDirectory, + $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"); + c.IncludeXmlComments(xmlPath, true); + }); + } + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Infrastructure/Installers/ServiceInstaller.cs b/src/Cheetah.WebApi/Infrastructure/Installers/ServiceInstaller.cs new file mode 100644 index 0000000..0115b4d --- /dev/null +++ b/src/Cheetah.WebApi/Infrastructure/Installers/ServiceInstaller.cs @@ -0,0 +1,48 @@ +using System.Net; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Prometheus; +using Cheetah.WebApi.Core.Config; +using Cheetah.WebApi.Shared.Infrastructure.Auth; +using Cheetah.WebApi.Shared.Config; +using Cheetah.Core.Interfaces; +using Cheetah.Core.Infrastructure.Services.OpenSearchClient; +using Cheetah.WebApi.Shared.Infrastructure.ServiceProvider; +using Cheetah.Core.Config; + +namespace Cheetah.WebApi.Infrastructure.Installers +{ + [InstallerPriority(Priorities.Default)] + public class ServiceInstaller : IServiceCollectionInstaller + { + public void Install(IServiceCollection services, IHostEnvironment hostEnvironment) + { + //Options + var configuration = services.BuildServiceProvider().GetRequiredService(); + services.Configure(configuration.GetSection(OAuthConfig.Position)); + services.Configure(configuration.GetSection(KafkaConfig.Position)); + services.Configure(configuration.GetSection(OpenSearchConfig.Position)); + services.Configure(configuration.GetSection(KafkaProducerConfig.Position)); + services.Configure(configuration.GetSection(KafkaConsumerConfig.Position)); + services.Configure(configuration.GetSection(PrometheusConfig.Position)); + + //Services + services.AddHttpContextAccessor(); + services.AddTransient(); + + // Hosted services + services.AddHostedService(); + + //Cache + services.AddMemoryCache(); + + //Httpclient + var applicationName = !string.IsNullOrEmpty(hostEnvironment.ApplicationName) ? hostEnvironment.ApplicationName : Dns.GetHostName(); + + services.AddHttpClient("", + (provider, client) => { client.DefaultRequestHeaders.Add("User-Agent", applicationName); }) + .UseHttpClientMetrics(); + } + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Presentation/Controllers/DefaultController.cs b/src/Cheetah.WebApi/Presentation/Controllers/DefaultController.cs new file mode 100644 index 0000000..fe6660e --- /dev/null +++ b/src/Cheetah.WebApi/Presentation/Controllers/DefaultController.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Cheetah.WebApi.Presentation.Controllers; + +[ApiExplorerSettings(IgnoreApi = true)] +public class DefaultController : ControllerBase +{ + [HttpGet("/")] + public RedirectResult RedirectToSwagger() + { + return RedirectPermanent("/swagger"); + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Presentation/Controllers/ErrorController.cs b/src/Cheetah.WebApi/Presentation/Controllers/ErrorController.cs new file mode 100644 index 0000000..553dc5e --- /dev/null +++ b/src/Cheetah.WebApi/Presentation/Controllers/ErrorController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; + +namespace Cheetah.WebApi.Presentation.Controllers; + +[ApiExplorerSettings(IgnoreApi = true)] +public class ErrorController : ControllerBase +{ + [HttpGet("/error-development")] + public IActionResult HandleErrorDevelopment( + [FromServices] IHostEnvironment hostEnvironment) + { + if (!hostEnvironment.IsDevelopment()) + { + return NotFound(); + } + + var exceptionHandlerFeature = + HttpContext.Features.Get()!; + + return Problem( + detail: exceptionHandlerFeature.Error.StackTrace, + title: exceptionHandlerFeature.Error.Message); + } + + [HttpGet("/error")] + public IActionResult HandleError() => + Problem(); +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Presentation/Controllers/KafkaController.cs b/src/Cheetah.WebApi/Presentation/Controllers/KafkaController.cs new file mode 100644 index 0000000..a466d03 --- /dev/null +++ b/src/Cheetah.WebApi/Presentation/Controllers/KafkaController.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Cheetah.WebApi.Core.Config; +using Confluent.Kafka; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +namespace Cheetah.WebApi.Presentation.Controllers +{ + [ApiController] + [ApiVersion("1.0")] + [Produces("application/json")] + [Route("api/v{version:apiVersion}/[controller]")] + public class KafkaController : ControllerBase + { + private readonly IConsumer _kafkaConsumer; + private readonly IProducer _kafkaProducer; + private readonly IOptions _kafkaProducerConfig; + + public KafkaController(IConsumer kafkaConsumer, + IProducer kafkaProducer, IOptions kafkaProducerConfig) + { + _kafkaProducerConfig = kafkaProducerConfig; + _kafkaConsumer = kafkaConsumer; + _kafkaProducer = kafkaProducer; + } + + /// + /// Consume a message from kafka + /// + /// + [HttpGet("consume")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetMessage() + { + var msg = _kafkaConsumer.Consume(TimeSpan.FromMilliseconds(100)); // todo: make configurable + if (msg?.Message == null) + { + return NotFound("No messages left!"); + } + else + { + return Ok(msg.Message?.Value); + } + } + + /// + /// Produce a message to kafka + /// + /// + [HttpPost("produce")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task ProductMessage(string message) + { + var msg = await _kafkaProducer.ProduceAsync(_kafkaProducerConfig.Value.Topic, new Message { Value = message }); + return Ok($"msg sent at offset: {msg.Offset.Value} for topic: {msg.Topic}"); + } + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Presentation/Controllers/OpenSearchController.cs b/src/Cheetah.WebApi/Presentation/Controllers/OpenSearchController.cs new file mode 100644 index 0000000..4e053d3 --- /dev/null +++ b/src/Cheetah.WebApi/Presentation/Controllers/OpenSearchController.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Cheetah.Core.Interfaces; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Cheetah.WebApi.Presentation.Controllers +{ + [ApiController] + [ApiVersion("1.0")] + [Produces("application/json")] + [Route("api/v{version:apiVersion}/[controller]")] + public class OpenSearchController : ControllerBase + { + private readonly ICheetahOpenSearchClient _opensearchNest; + + public OpenSearchController(ICheetahOpenSearchClient nestClient) + { + _opensearchNest = nestClient; + } + + /// + /// Retrieves all indices on the corresponding OpenSearch instance + /// + /// + [HttpGet("indices")] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task GetIndices() + { + var indicies = await _opensearchNest.GetIndices(); + return Ok(indicies); + } + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/Program.cs b/src/Cheetah.WebApi/Program.cs new file mode 100644 index 0000000..59ed809 --- /dev/null +++ b/src/Cheetah.WebApi/Program.cs @@ -0,0 +1,121 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using Cheetah.WebApi.Shared.Infrastructure.ServiceProvider; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.IdentityModel.Logging; +using Prometheus; +using Serilog; +using Serilog.Events; +using Serilog.Formatting.Compact; + +namespace Cheetah.WebApi +{ + public class Program + { + public static void Main(string[] args) + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .Enrich.FromLogContext() + .WriteTo.Console() + .CreateBootstrapLogger(); + try + { + IdentityModelEventSource.ShowPII = true; + + var builder = WebApplication.CreateBuilder(args); + + builder.Host.UseSerilog((ctx, lc) => + { + lc.Enrich.FromLogContext() + .Enrich.WithProperty("AppName", ctx.HostingEnvironment.ApplicationName) + .Enrich.WithProperty("Environment", ctx.HostingEnvironment.EnvironmentName) + .Enrich.WithProperty("AssemblyVersion", Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "1.0.0") + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .MinimumLevel.Override("System", LogEventLevel.Error) + .MinimumLevel.Override("System.Net.Http.HttpClient", LogEventLevel.Warning) + .WriteTo.Console(new RenderedCompactJsonFormatter()); + + lc.MinimumLevel.Debug(); + + if (Debugger.IsAttached) + { + Serilog.Debugging.SelfLog.Enable(Console.WriteLine); + } + }, true); + + //Use custom DI installers + builder.Services.Install(builder.Environment, Assembly.GetAssembly(typeof(AssemblyAnchor))!); + + // Add hosted services + builder.Services.AddHostedService(); + + var app = builder.Build(); + var apiVersionDescriptionProvider = app.Services.GetRequiredService(); + + // It's important that the UseSerilogRequestLogging() call appears before handlers such as MVC. + // The middleware will not time or log components that appear before it in the pipeline. + // ref: https://github.com/serilog/serilog-aspnetcore + app.UseSerilogRequestLogging(); + app.UseHttpMetrics(m => m.CaptureMetricsUrl = false); + + // Configure the HTTP request pipeline. + app.UseSwagger(); + app.UseSwaggerUI(c => + { + // build a swagger endpoint for each discovered API version + foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions) + { + c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); + } + }); + + + app.UseCors(corsPolicyBuilder => corsPolicyBuilder + .AllowAnyMethod() + .AllowAnyHeader() + .AllowAnyOrigin()); + + app.MapHealthChecks("/health", + new HealthCheckOptions + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + app.MapHealthChecks("/health/ready", + new HealthCheckOptions() { Predicate = (check) => check.Tags.Contains("ready") }); + + app.UseExceptionHandler(app.Environment.IsDevelopment() ? "/error-development" : "/error"); + + app.UseRouting(); + + + app.UseAuthorization(); + app.MapControllers(); + + app.UseEndpoints(endpoints => + { + endpoints.MapMetrics(); + }); + + app.Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "Web host terminated unexpectedly"); + } + + finally + { + Log.CloseAndFlush(); + } + } + } +} diff --git a/src/Cheetah.WebApi/Properties/launchSettings.json b/src/Cheetah.WebApi/Properties/launchSettings.json new file mode 100644 index 0000000..e7472c9 --- /dev/null +++ b/src/Cheetah.WebApi/Properties/launchSettings.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:25719", + "sslPort": 44337 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "publishAllPorts": true, + "useSSL": true + }, + "WSL": { + "commandName": "WSL2", + "distributionName": "" + } + } +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/appsettings.json b/src/Cheetah.WebApi/appsettings.json new file mode 100644 index 0000000..a8ef981 --- /dev/null +++ b/src/Cheetah.WebApi/appsettings.json @@ -0,0 +1,7 @@ +{ + "Prometheus": { + "Url": "/metrics", + "port": 1854 + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/src/Cheetah.WebApi/swagger.json b/src/Cheetah.WebApi/swagger.json new file mode 100644 index 0000000..68e346c --- /dev/null +++ b/src/Cheetah.WebApi/swagger.json @@ -0,0 +1,142 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Cheetah.WebApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", + "version": "1.0" + }, + "paths": { + "/api/v1/kafka/consume": { + "get": { + "tags": [ + "Kafka" + ], + "summary": "Consume a message from kafka", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/api/v1/kafka/produce": { + "post": { + "tags": [ + "Kafka" + ], + "summary": "Produce a message to kafka", + "parameters": [ + { + "name": "message", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/api/v1/opensearch/indices": { + "get": { + "tags": [ + "OpenSearch" + ], + "summary": "Retrieves all indices on the corresponding OpenSearch instance", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ProblemDetails": { + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "status": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "instance": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": { } + } + } + } +} \ No newline at end of file diff --git a/src/NuGet-CI.Config b/src/NuGet-CI.Config new file mode 100644 index 0000000..b916986 --- /dev/null +++ b/src/NuGet-CI.Config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/docker-compose.yml b/src/docker-compose.yml new file mode 100644 index 0000000..23e37d6 --- /dev/null +++ b/src/docker-compose.yml @@ -0,0 +1,57 @@ +--- +version: "3.4" + +services: + cheetah.webapi: + image: ${DOCKER_REGISTRY-}cheetahwebapi + hostname: cheetahwebapi + healthcheck: + test: ["CMD", "curl", "-f", "http://cheetahwebapi:80/health"] + build: + context: ../ + dockerfile: src/Cheetah.WebApi/Dockerfile + secrets: + - GITHUB_ACTOR + - GITHUB_TOKEN + environment: + - ASPNETCORE_ENVIRONMENT=Development + #- "ASPNETCORE_URLS=https://+:1851;http://+:1751" + #- "ASPNETCORE_Kestrel__Certificates__Default__Password=password" + #- "ASPNETCORE_Kestrel__Certificates__Default__Path=/root/.aspnet/https/aspnetapp.pfx" + - "Prometheus__port=1861" + - "OpenSearch__Url=http://opensearch:9200" + + - "OpenSearch__ClientId=cheetahwebapi" + - "OpenSearch__ClientSecret=admin" + - "OpenSearch__AuthMode=OAuth2" + - "OpenSearch__OAuthScope=cheetahwebapi" + - "OpenSearch__TokenEndpoint=http://cheetahoauthsimulator:80/oauth2/token" + + - "Kafka__ClientId=cheetahwebalertservice" + - "Kafka__ClientSecret=1234" + - "Kafka__AuthMode=OAuth2" + - "Kafka__OAuthScope=cheetahwebalertservice" + - "Kafka__TokenEndpoint=http://cheetahoauthsimulator:80/oauth2/token" + - "Kafka__Url=kafka:19092" + + - "KafkaProducerConfig__Topic=cheetahwebapi" + - "KafkaConsumerConfig__Topic=cheetahwebapi" + + networks: + - cheetah-infrastructure + ports: + - 1751:80 + # - "1851:1851" + - "1861:1861" + #volumes: + # - "${APPDATA:-.}/ASP.NET/https:/root/.aspnet/https:ro" + +networks: + cheetah-infrastructure: + external: true + +secrets: + GITHUB_TOKEN: + environment: GITHUB_TOKEN + GITHUB_ACTOR: + environment: GITHUB_ACTOR