├── .github ├── dependabot.yml └── workflows │ ├── build-and-dockerize.yml │ ├── codeql.yml │ └── docker.yml ├── .gitignore ├── .mergify.yml ├── .vscode └── settings.json ├── BervProject.WebApi.Boilerplate.AppHost ├── BervProject.WebApi.Boilerplate.AppHost.csproj ├── Program.cs ├── appsettings.Development.json └── appsettings.json ├── BervProject.WebApi.Boilerplate.MigrationService ├── BervProject.WebApi.Boilerplate.MigrationService.csproj ├── Program.cs ├── Worker.cs ├── appsettings.Development.json └── appsettings.json ├── BervProject.WebApi.Boilerplate.ServiceDefaults ├── BervProject.WebApi.Boilerplate.ServiceDefaults.csproj └── Extensions.cs ├── BervProject.WebApi.Boilerplate.ndproj ├── BervProject.WebApi.Boilerplate.sln ├── BervProject.WebApi.Boilerplate ├── .gitignore ├── BervProject.WebApi.Boilerplate.csproj ├── ConfigModel │ ├── AWSConfiguration.cs │ └── AzureConfiguration.cs ├── Controllers │ ├── AWSS3Controller.cs │ ├── BlobController.cs │ ├── CronController.cs │ ├── DynamoController.cs │ ├── EmailController.cs │ ├── ErrorController.cs │ ├── NoteController.cs │ ├── ServiceBusSenderController.cs │ ├── StorageQueueController.cs │ └── WeatherForecastController.cs ├── Entities │ ├── Book.cs │ ├── Note.cs │ └── Publisher.cs ├── EntityFramework │ └── BoilerplateDbContext.cs ├── Extensions │ ├── SetupAWSExtension.cs │ └── SetupAzureExtension.cs ├── Migrations │ ├── 20210118042709_NewBookDb.Designer.cs │ ├── 20210118042709_NewBookDb.cs │ └── BoilerplateDbContextModelSnapshot.cs ├── Models │ ├── EmailSendRequest.cs │ ├── MessageData.cs │ ├── Request │ │ └── UploadFile.cs │ ├── Response │ │ └── MessageSenderResponse.cs │ └── WeatherForecast.cs ├── NLog.config ├── Program.cs ├── Services │ ├── AWS │ │ ├── AWSS3Service.cs │ │ ├── DynamoDbServices.cs │ │ ├── EmailService.cs │ │ ├── IAWSS3Service.cs │ │ ├── IDynamoDbServices.cs │ │ └── IEmailService.cs │ ├── Azure │ │ ├── AzureQueueServices.cs │ │ ├── AzureStorageQueueService.cs │ │ ├── AzureTableStorageService.cs │ │ ├── BlobService.cs │ │ ├── IAzureQueueServices.cs │ │ ├── IAzureStorageQueueService.cs │ │ ├── IAzureTableStorageService.cs │ │ ├── IBlobService.cs │ │ ├── IServiceBusQueueConsumer.cs │ │ ├── IServiceBusTopicSubscription.cs │ │ ├── ITopicServices.cs │ │ ├── ServiceBusQueueConsumer.cs │ │ ├── ServiceBusTopicSubscription.cs │ │ └── TopicServices.cs │ ├── CronService.cs │ ├── ICronService.cs │ ├── IProcessData.cs │ └── ProcessData.cs ├── appsettings.Development.json └── appsettings.json ├── BervProject.WebApi.Integration.Test ├── BervProject.WebApi.Integration.Test.csproj ├── BlobControllerTest.cs ├── Collections │ └── WebAppCollection.cs ├── CronControllerTest.cs ├── Docs │ └── test.txt ├── ErrorControllerTest.cs ├── Fixtures │ └── WebAppFixture.cs ├── HealthCheckTest.cs ├── NoteControllerTest.cs ├── StorageQueueControllerTest.cs ├── Usings.cs └── WeatherForecastControllerTest.cs ├── BervProject.WebApi.Test ├── BervProject.WebApi.Test.csproj ├── Services │ ├── AWS │ │ ├── AWSS3ServiceTest.cs │ │ ├── DynamoDbServicesTest.cs │ │ └── EmailServiceTest.cs │ ├── Azure │ │ ├── AzureQueueServicesTest.cs │ │ ├── AzureStorageQueueServiceTest.cs │ │ ├── BlobServiceTest.cs │ │ └── TopicServiceTest.cs │ ├── CronServiceTest.cs │ └── ProcessDataTest.cs └── WeatherForecastTest.cs ├── Dockerfile ├── LICENSE ├── README.md └── azure-pipelines.yml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "08:00" 8 | timezone: Asia/Jakarta 9 | open-pull-requests-limit: 100 10 | groups: 11 | awssdk: 12 | patterns: 13 | - "AWSSDK*" 14 | azure: 15 | patterns: 16 | - "Azure*" 17 | hangfire: 18 | patterns: 19 | - "Hangfire*" 20 | nlog: 21 | patterns: 22 | - "NLog*" 23 | 24 | - package-ecosystem: github-actions 25 | directory: "/" 26 | schedule: 27 | interval: weekly 28 | day: saturday 29 | time: "08:00" 30 | timezone: Asia/Jakarta 31 | open-pull-requests-limit: 100 32 | reviewers: 33 | - berviantoleo 34 | assignees: 35 | - berviantoleo 36 | 37 | - package-ecosystem: docker 38 | directory: "/" 39 | schedule: 40 | interval: weekly 41 | day: saturday 42 | time: "08:00" 43 | timezone: Asia/Jakarta 44 | open-pull-requests-limit: 100 45 | reviewers: 46 | - berviantoleo 47 | assignees: 48 | - berviantoleo 49 | -------------------------------------------------------------------------------- /.github/workflows/build-and-dockerize.yml: -------------------------------------------------------------------------------- 1 | name: Build .NET 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | env: 8 | ConnectionStrings__BoilerplateConnectionString: "Host=localhost;Database=testdb;Username=postgres;Password=postgres" 9 | jobs: 10 | build: 11 | # Service containers to run with `container-job` 12 | services: 13 | # Label used to access the service container 14 | postgres: 15 | # Docker Hub image 16 | image: postgres:17-alpine 17 | ports: 18 | - 5432:5432 19 | # Provide the password for postgres 20 | env: 21 | POSTGRES_PASSWORD: postgres 22 | POSTGRES_DB: testdb 23 | # Set health checks to wait until postgres has started 24 | options: >- 25 | --health-cmd pg_isready 26 | --health-interval 10s 27 | --health-timeout 5s 28 | --health-retries 5 29 | azurite: 30 | image: mcr.microsoft.com/azure-storage/azurite 31 | ports: 32 | - 10000:10000 33 | - 10001:10001 34 | - 10002:10002 35 | runs-on: ubuntu-24.04 36 | steps: 37 | - uses: actions/checkout@v4 38 | - name: Setup .NET Core 39 | uses: actions/setup-dotnet@v4 40 | with: 41 | dotnet-version: '9.0.x' 42 | - name: Install dependencies 43 | run: dotnet restore 44 | - name: Build 45 | run: dotnet build --configuration Release --no-restore 46 | - name: Migrate DB 47 | run: | 48 | dotnet tool install --global dotnet-ef 49 | dotnet tool restore 50 | dotnet ef database update --startup-project BervProject.WebApi.Boilerplate --project BervProject.WebApi.Boilerplate 51 | - name: Test 52 | run: dotnet test --configuration Release --no-restore --no-build --verbosity normal --collect:"XPlat Code Coverage" 53 | - name: Report Codecov 54 | uses: codecov/codecov-action@v5 55 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | # The branches below must be a subset of the branches above 7 | branches: [ main ] 8 | schedule: 9 | - cron: '30 1 * * 6' 10 | jobs: 11 | codeql: 12 | name: Analyze 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | runs-on: ubuntu-24.04 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Initialize CodeQL 21 | uses: github/codeql-action/init@v3 22 | with: 23 | languages: csharp 24 | - name: Setup .NET Core 25 | uses: actions/setup-dotnet@v4 26 | with: 27 | dotnet-version: '9.0.x' 28 | dotnet-quality: 'preview' 29 | - name: Autobuild 30 | uses: github/codeql-action/autobuild@v3 31 | - name: Perform CodeQL Analysis 32 | uses: github/codeql-action/analyze@v3 33 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Dockerize 2 | on: 3 | push: 4 | branches: [ main ] 5 | tags: 6 | - v* 7 | pull_request: 8 | branches: [ main ] 9 | env: 10 | IMAGE_NAME: netcoreboilerplate 11 | jobs: 12 | build-docker-test: 13 | runs-on: ubuntu-24.04 14 | if: github.event_name == 'pull_request' 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: hadolint/hadolint-action@v3.1.0 18 | name: Lint Dockerfile 19 | with: 20 | dockerfile: Dockerfile 21 | failure-threshold: error 22 | - name: Build Image 23 | run: docker build . -t $IMAGE_NAME 24 | - name: Run Trivy vulnerability scanner 25 | uses: aquasecurity/trivy-action@master 26 | with: 27 | image-ref: ${{ env.IMAGE_NAME }} 28 | format: 'sarif' 29 | output: 'trivy-results.sarif' 30 | - name: Upload Trivy scan results to GitHub Security tab 31 | uses: github/codeql-action/upload-sarif@v3 32 | with: 33 | sarif_file: 'trivy-results.sarif' 34 | build-push-docker: 35 | runs-on: ubuntu-24.04 36 | if: github.event_name == 'push' 37 | steps: 38 | - uses: actions/checkout@v4 39 | - name: Set up QEMU 40 | uses: docker/setup-qemu-action@v3 41 | - name: Set up Docker Buildx 42 | uses: docker/setup-buildx-action@v3 43 | with: 44 | platforms: linux/amd64,linux/arm64 45 | - name: Log in to Docker Hub 46 | uses: docker/login-action@v3 47 | with: 48 | username: ${{ secrets.DOCKER_USERNAME }} 49 | password: ${{ secrets.DOCKER_TOKEN }} 50 | - name: Log in to the Container registry 51 | uses: docker/login-action@v3 52 | with: 53 | registry: ghcr.io 54 | username: ${{ github.actor }} 55 | password: ${{ secrets.GITHUB_TOKEN }} 56 | - name: Extract metadata (tags, labels) for Docker 57 | id: meta 58 | uses: docker/metadata-action@v5 59 | with: 60 | images: | 61 | bervproject/${{ env.IMAGE_NAME }} 62 | ghcr.io/${{ github.repository }} 63 | - name: Build and push Docker images 64 | uses: docker/build-push-action@v6 65 | with: 66 | context: . 67 | push: true 68 | tags: ${{ steps.meta.outputs.tags }} 69 | labels: ${{ steps.meta.outputs.labels }} 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | /NDependOut 332 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: automatic merge for Dependabot pull requests 3 | conditions: 4 | - author=dependabot[bot] 5 | actions: 6 | merge: 7 | method: squash 8 | review: 9 | type: APPROVE 10 | message: Automatically approving dependabot 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.customTags": [ 3 | "!And", 4 | "!And sequence", 5 | "!If", 6 | "!If sequence", 7 | "!Not", 8 | "!Not sequence", 9 | "!Equals", 10 | "!Equals sequence", 11 | "!Or", 12 | "!Or sequence", 13 | "!FindInMap", 14 | "!FindInMap sequence", 15 | "!Base64", 16 | "!Join", 17 | "!Join sequence", 18 | "!Cidr", 19 | "!Ref", 20 | "!Sub", 21 | "!Sub sequence", 22 | "!GetAtt", 23 | "!GetAZs", 24 | "!ImportValue", 25 | "!ImportValue sequence", 26 | "!Select", 27 | "!Select sequence", 28 | "!Split", 29 | "!Split sequence" 30 | ] 31 | } -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.AppHost/BervProject.WebApi.Boilerplate.AppHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exe 6 | net9.0 7 | enable 8 | enable 9 | true 10 | 0f1966e4-7b2b-4a28-a9b6-8198aab433ec 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.AppHost/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = DistributedApplication.CreateBuilder(args); 2 | 3 | var cache = builder.AddRedis("cache").WithRedisInsight(); 4 | var postgres = builder.AddPostgres("postgres").WithPgAdmin(); 5 | var postgresdb = postgres.AddDatabase("postgresdb"); 6 | var serviceBus = builder.AddAzureServiceBus("messaging").RunAsEmulator(); 7 | var storage = builder.AddAzureStorage("storage").RunAsEmulator(); 8 | var blobs = storage.AddBlobs("blobs"); 9 | var queues = storage.AddQueues("queues"); 10 | var tables = storage.AddTables("tables"); 11 | 12 | var migration = builder.AddProject("migrations") 13 | .WithReference(postgresdb, connectionName: "BoilerplateConnectionString") 14 | .WithExplicitStart(); 15 | 16 | builder.AddProject("apiservice") 17 | .WithHttpEndpoint() 18 | .WithReference(cache, connectionName: "Redis") 19 | .WithReference(postgresdb, connectionName: "BoilerplateConnectionString") 20 | .WithReference(blobs, connectionName: "AzureStorageBlob") 21 | .WithReference(queues, connectionName: "AzureStorageQueue") 22 | .WithReference(tables, connectionName: "AzureStorageTable") 23 | .WithReference(serviceBus, connectionName: "AzureServiceBus") 24 | .WaitFor(cache) 25 | .WaitFor(postgresdb) 26 | .WaitFor(blobs) 27 | .WaitFor(queues) 28 | .WaitFor(tables) 29 | .WaitFor(serviceBus) 30 | .WaitForCompletion(migration); 31 | 32 | builder.Build().Run(); 33 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.AppHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.AppHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "Aspire.Hosting.Dcp": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.MigrationService/BervProject.WebApi.Boilerplate.MigrationService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | dotnet-BervProject.WebApi.Boilerplate.MigrationService-02d1add9-9cd8-4b0e-96ab-9dedc82c871c 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.MigrationService/Program.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.EntityFramework; 2 | using BervProject.WebApi.Boilerplate.MigrationService; 3 | 4 | var builder = Host.CreateApplicationBuilder(args); 5 | 6 | builder.AddServiceDefaults(); 7 | 8 | builder.Services.AddHostedService(); 9 | 10 | builder.Services.AddOpenTelemetry() 11 | .WithTracing(tracing => tracing.AddSource(Worker.ActivitySourceName)); 12 | builder.AddNpgsqlDbContext("BoilerplateConnectionString"); 13 | 14 | var host = builder.Build(); 15 | host.Run(); 16 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.MigrationService/Worker.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using BervProject.WebApi.Boilerplate.EntityFramework; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace BervProject.WebApi.Boilerplate.MigrationService; 6 | 7 | public class Worker : BackgroundService 8 | { 9 | public const string ActivitySourceName = "Migrations"; 10 | private static readonly ActivitySource SActivitySource = new(ActivitySourceName); 11 | 12 | private readonly IServiceProvider _serviceProvider; 13 | private readonly IHostApplicationLifetime _hostApplicationLifetime; 14 | 15 | public Worker(IServiceProvider serviceProvider, 16 | IHostApplicationLifetime hostApplicationLifetime) 17 | { 18 | _serviceProvider = serviceProvider; 19 | _hostApplicationLifetime = hostApplicationLifetime; 20 | } 21 | 22 | protected override async Task ExecuteAsync(CancellationToken cancellationToken) 23 | { 24 | using var activity = SActivitySource.StartActivity("Migrating database", ActivityKind.Client); 25 | 26 | try 27 | { 28 | using var scope = _serviceProvider.CreateScope(); 29 | var dbContext = scope.ServiceProvider.GetRequiredService(); 30 | 31 | await RunMigrationAsync(dbContext, cancellationToken); 32 | } 33 | catch (Exception ex) 34 | { 35 | activity?.AddException(ex); 36 | throw; 37 | } 38 | 39 | _hostApplicationLifetime.StopApplication(); 40 | } 41 | 42 | private static async Task RunMigrationAsync(BoilerplateDbContext dbContext, CancellationToken cancellationToken) 43 | { 44 | var strategy = dbContext.Database.CreateExecutionStrategy(); 45 | await strategy.ExecuteAsync(async () => 46 | { 47 | // Run migration in a transaction to avoid partial migration if it fails. 48 | await dbContext.Database.MigrateAsync(cancellationToken); 49 | }); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.MigrationService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.MigrationService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.ServiceDefaults/BervProject.WebApi.Boilerplate.ServiceDefaults.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.ServiceDefaults/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Diagnostics.HealthChecks; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.Extensions.ServiceDiscovery; 7 | using OpenTelemetry; 8 | using OpenTelemetry.Metrics; 9 | using OpenTelemetry.Trace; 10 | 11 | namespace Microsoft.Extensions.Hosting; 12 | 13 | // Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. 14 | // This project should be referenced by each service project in your solution. 15 | // To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults 16 | public static class Extensions 17 | { 18 | public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) 19 | { 20 | builder.ConfigureOpenTelemetry(); 21 | 22 | builder.AddDefaultHealthChecks(); 23 | 24 | builder.Services.AddServiceDiscovery(); 25 | 26 | builder.Services.ConfigureHttpClientDefaults(http => 27 | { 28 | // Turn on resilience by default 29 | http.AddStandardResilienceHandler(); 30 | 31 | // Turn on service discovery by default 32 | http.AddServiceDiscovery(); 33 | }); 34 | 35 | // Uncomment the following to restrict the allowed schemes for service discovery. 36 | // builder.Services.Configure(options => 37 | // { 38 | // options.AllowedSchemes = ["https"]; 39 | // }); 40 | 41 | return builder; 42 | } 43 | 44 | public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) 45 | { 46 | builder.Logging.AddOpenTelemetry(logging => 47 | { 48 | logging.IncludeFormattedMessage = true; 49 | logging.IncludeScopes = true; 50 | }); 51 | 52 | builder.Services.AddOpenTelemetry() 53 | .WithMetrics(metrics => 54 | { 55 | metrics.AddAspNetCoreInstrumentation() 56 | .AddHttpClientInstrumentation() 57 | .AddRuntimeInstrumentation(); 58 | }) 59 | .WithTracing(tracing => 60 | { 61 | tracing.AddAspNetCoreInstrumentation() 62 | // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) 63 | //.AddGrpcClientInstrumentation() 64 | .AddHttpClientInstrumentation(); 65 | }); 66 | 67 | builder.AddOpenTelemetryExporters(); 68 | 69 | return builder; 70 | } 71 | 72 | private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) 73 | { 74 | var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); 75 | 76 | if (useOtlpExporter) 77 | { 78 | builder.Services.AddOpenTelemetry().UseOtlpExporter(); 79 | } 80 | 81 | // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) 82 | //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) 83 | //{ 84 | // builder.Services.AddOpenTelemetry() 85 | // .UseAzureMonitor(); 86 | //} 87 | 88 | return builder; 89 | } 90 | 91 | public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) 92 | { 93 | builder.Services.AddHealthChecks() 94 | // Add a default liveness check to ensure app is responsive 95 | .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); 96 | 97 | return builder; 98 | } 99 | 100 | public static WebApplication MapDefaultEndpoints(this WebApplication app) 101 | { 102 | // Adding health checks endpoints to applications in non-development environments has security implications. 103 | // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. 104 | if (app.Environment.IsDevelopment()) 105 | { 106 | // All health checks must pass for app to be considered ready to accept traffic after starting 107 | app.MapHealthChecks("/health"); 108 | 109 | // Only health checks tagged with the "live" tag must pass for app to be considered alive 110 | app.MapHealthChecks("/alive", new HealthCheckOptions 111 | { 112 | Predicate = r => r.Tags.Contains("live") 113 | }); 114 | } 115 | 116 | return app; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BervProject.WebApi.Boilerplate", "BervProject.WebApi.Boilerplate\BervProject.WebApi.Boilerplate.csproj", "{61F9CED4-DF01-40F5-A126-DE9F73C7AA89}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BervProject.WebApi.Test", "BervProject.WebApi.Test\BervProject.WebApi.Test.csproj", "{CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CB53F077-669D-465F-A871-642F25CC3194}" 11 | ProjectSection(SolutionItems) = preProject 12 | azure-pipelines.yml = azure-pipelines.yml 13 | .github\workflows\build-and-dockerize.yml = .github\workflows\build-and-dockerize.yml 14 | .github\workflows\codeql.yml = .github\workflows\codeql.yml 15 | .github\dependabot.yml = .github\dependabot.yml 16 | Dockerfile = Dockerfile 17 | README.md = README.md 18 | EndProjectSection 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Integration.Test", "BervProject.WebApi.Integration.Test\BervProject.WebApi.Integration.Test.csproj", "{B1139AC3-3DC2-453D-9A72-9C7196F9759C}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Boilerplate.AppHost", "BervProject.WebApi.Boilerplate.AppHost\BervProject.WebApi.Boilerplate.AppHost.csproj", "{D6E4407A-586B-4D75-A5F7-5AC9E7016614}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Boilerplate.ServiceDefaults", "BervProject.WebApi.Boilerplate.ServiceDefaults\BervProject.WebApi.Boilerplate.ServiceDefaults.csproj", "{3E0AA90A-F50F-4226-8236-947B6DC82BCC}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Boilerplate.MigrationService", "BervProject.WebApi.Boilerplate.MigrationService\BervProject.WebApi.Boilerplate.MigrationService.csproj", "{94B60493-45E4-4437-9155-3D30DFBFB66A}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Debug|x64 = Debug|x64 32 | Debug|x86 = Debug|x86 33 | Release|Any CPU = Release|Any CPU 34 | Release|x64 = Release|x64 35 | Release|x86 = Release|x86 36 | EndGlobalSection 37 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 38 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x64.ActiveCfg = Debug|Any CPU 41 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x64.Build.0 = Debug|Any CPU 42 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x86.ActiveCfg = Debug|Any CPU 43 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x86.Build.0 = Debug|Any CPU 44 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x64.ActiveCfg = Release|Any CPU 47 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x64.Build.0 = Release|Any CPU 48 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x86.ActiveCfg = Release|Any CPU 49 | {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x86.Build.0 = Release|Any CPU 50 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x64.Build.0 = Debug|Any CPU 54 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x86.ActiveCfg = Debug|Any CPU 55 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x86.Build.0 = Debug|Any CPU 56 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x64.ActiveCfg = Release|Any CPU 59 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x64.Build.0 = Release|Any CPU 60 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x86.ActiveCfg = Release|Any CPU 61 | {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x86.Build.0 = Release|Any CPU 62 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x64.ActiveCfg = Debug|Any CPU 65 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x64.Build.0 = Debug|Any CPU 66 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x86.ActiveCfg = Debug|Any CPU 67 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x86.Build.0 = Debug|Any CPU 68 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x64.ActiveCfg = Release|Any CPU 71 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x64.Build.0 = Release|Any CPU 72 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x86.ActiveCfg = Release|Any CPU 73 | {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x86.Build.0 = Release|Any CPU 74 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x64.ActiveCfg = Debug|Any CPU 77 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x64.Build.0 = Debug|Any CPU 78 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x86.ActiveCfg = Debug|Any CPU 79 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x86.Build.0 = Debug|Any CPU 80 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|Any CPU.ActiveCfg = Release|Any CPU 81 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|Any CPU.Build.0 = Release|Any CPU 82 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x64.ActiveCfg = Release|Any CPU 83 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x64.Build.0 = Release|Any CPU 84 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x86.ActiveCfg = Release|Any CPU 85 | {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x86.Build.0 = Release|Any CPU 86 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 87 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|Any CPU.Build.0 = Debug|Any CPU 88 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x64.ActiveCfg = Debug|Any CPU 89 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x64.Build.0 = Debug|Any CPU 90 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x86.ActiveCfg = Debug|Any CPU 91 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x86.Build.0 = Debug|Any CPU 92 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|Any CPU.ActiveCfg = Release|Any CPU 93 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|Any CPU.Build.0 = Release|Any CPU 94 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x64.ActiveCfg = Release|Any CPU 95 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x64.Build.0 = Release|Any CPU 96 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x86.ActiveCfg = Release|Any CPU 97 | {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x86.Build.0 = Release|Any CPU 98 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 99 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|Any CPU.Build.0 = Debug|Any CPU 100 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x64.ActiveCfg = Debug|Any CPU 101 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x64.Build.0 = Debug|Any CPU 102 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x86.ActiveCfg = Debug|Any CPU 103 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x86.Build.0 = Debug|Any CPU 104 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|Any CPU.ActiveCfg = Release|Any CPU 105 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|Any CPU.Build.0 = Release|Any CPU 106 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x64.ActiveCfg = Release|Any CPU 107 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x64.Build.0 = Release|Any CPU 108 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x86.ActiveCfg = Release|Any CPU 109 | {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x86.Build.0 = Release|Any CPU 110 | EndGlobalSection 111 | GlobalSection(SolutionProperties) = preSolution 112 | HideSolutionNode = FALSE 113 | EndGlobalSection 114 | GlobalSection(ExtensibilityGlobals) = postSolution 115 | SolutionGuid = {95F60BDF-4C1C-46AA-816D-E645235051C5} 116 | EndGlobalSection 117 | GlobalSection(NDepend) = preSolution 118 | Project = ".\BervProject.WebApi.Boilerplate.ndproj" 119 | EndGlobalSection 120 | EndGlobal 121 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | 332 | logs/ 333 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/BervProject.WebApi.Boilerplate.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | BervProject.WebApi.Boilerplate 6 | true 7 | 8 | 9 | 10 | bin\Debug\BervProject.WebApi.Boilerplate.xml 11 | 12 | 13 | 14 | bin\Release\BervProject.WebApi.Boilerplate.xml 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | all 38 | runtime; build; native; contentfiles; analyzers; buildtransitive 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/ConfigModel/AWSConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.ConfigModel 2 | { 3 | public class AWSConfiguration 4 | { 5 | public AWSBasicConfiguration Basic { get; set; } 6 | public AWSEmailConfiguration Email { get; set; } 7 | public AWSDynamoConfiguration Dynamo { get; set; } 8 | } 9 | 10 | public class AWSAuth 11 | { 12 | public string AccessKey { get; set; } 13 | public string SecretKey { get; set; } 14 | } 15 | 16 | public class AWSBasicConfiguration 17 | { 18 | public AWSAuth Auth { get; set; } 19 | } 20 | 21 | public class AWSDynamoConfiguration 22 | { 23 | public string Location { get; set; } 24 | } 25 | 26 | public class AWSEmailConfiguration 27 | { 28 | public string Location { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/ConfigModel/AzureConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.ConfigModel 2 | { 3 | public class AzureConfiguration 4 | { 5 | public AzureServiceBus ServiceBus { get; set; } 6 | public AzureStorage Storage { get; set; } 7 | } 8 | 9 | public class AzureStorage 10 | { 11 | public StorageQueue Queue { get; set; } 12 | public BlobStorage Blob { get; set; } 13 | } 14 | 15 | public class BlobStorage 16 | { 17 | public string ContainerName { get; set; } 18 | } 19 | 20 | public class StorageQueue 21 | { 22 | public string QueueName { get; set; } 23 | } 24 | 25 | public class AzureServiceBus 26 | { 27 | public string QueueName { get; set; } 28 | public string TopicName { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/AWSS3Controller.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using BervProject.WebApi.Boilerplate.Services.AWS; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace BervProject.WebApi.Boilerplate.Controllers 8 | { 9 | [ApiController] 10 | [Route("api/v{version:apiVersion}/[controller]")] 11 | [ApiVersion("1.0")] 12 | public class SThreeController : ControllerBase 13 | { 14 | private readonly IAWSS3Service _awsS3Service; 15 | public SThreeController(IAWSS3Service awsS3Service) 16 | { 17 | _awsS3Service = awsS3Service; 18 | } 19 | 20 | /// 21 | /// Upload to S3 22 | /// 23 | /// 24 | /// 25 | [HttpPost("upload")] 26 | [ProducesResponseType(typeof(Dictionary), StatusCodes.Status200OK)] 27 | public async Task Upload([FromForm] IFormFile file) 28 | { 29 | var result = await _awsS3Service.UploadFile(file); 30 | return Ok(new 31 | { 32 | path = result 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/BlobController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection.Metadata; 4 | using BervProject.WebApi.Boilerplate.Models.Request; 5 | using BervProject.WebApi.Boilerplate.Services.Azure; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace BervProject.WebApi.Boilerplate.Controllers 11 | { 12 | [ApiController] 13 | [Route("api/v{version:apiVersion}/[controller]")] 14 | [ApiVersion("1.0")] 15 | public class BlobController : ControllerBase 16 | { 17 | private readonly IBlobService _blobService; 18 | private readonly ILogger _logger; 19 | 20 | /// 21 | /// Blob Controller Constructor 22 | /// 23 | /// 24 | /// 25 | public BlobController(IBlobService blobService, ILogger logger) 26 | { 27 | _blobService = blobService; 28 | _logger = logger; 29 | } 30 | 31 | /// 32 | /// Create Blob Container 33 | /// 34 | /// true/false 35 | [HttpPost("create")] 36 | [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] 37 | public IActionResult CreateBlobContainer() 38 | { 39 | _blobService.CreateStorageContainer(); 40 | return Ok(true); 41 | } 42 | 43 | /// 44 | /// Return list of blob 45 | /// 46 | /// 47 | [HttpGet("list")] 48 | [Produces("application/json")] 49 | [ProducesResponseType(typeof(List>), StatusCodes.Status200OK)] 50 | public IActionResult ListBlob() 51 | { 52 | var info = _blobService.GetBlobsInfo(); 53 | return Ok(info); 54 | } 55 | 56 | /// 57 | /// Upload Blob 58 | /// 59 | /// 60 | /// 61 | [HttpPost("upload")] 62 | [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] 63 | public IActionResult Upload([FromForm] UploadFile uploadFile) 64 | { 65 | _blobService.UploadFile(uploadFile.File); 66 | return Ok(true); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/CronController.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Services; 2 | using Hangfire; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace BervProject.WebApi.Boilerplate.Controllers 8 | { 9 | [ApiController] 10 | [Route("api/v{version:apiVersion}/[controller]")] 11 | [ApiVersion("1.0")] 12 | public class CronController : ControllerBase 13 | { 14 | /// 15 | /// Create Cron Once 16 | /// 17 | /// 18 | /// 19 | [HttpPost("CreateCronOnce")] 20 | [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] 21 | 22 | public IActionResult CreateCronOnce([FromServices] IBackgroundJobClient backgroundJobClient) 23 | { 24 | var jobId = backgroundJobClient.Enqueue((x) => x.HelloWorld()); 25 | return Ok(jobId); 26 | } 27 | 28 | /// 29 | /// Create Cron Recurance 30 | /// 31 | /// 32 | /// 33 | [HttpPost("CreateRecurance")] 34 | [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] 35 | public IActionResult CreateRecurance([FromServices] IRecurringJobManager recurringJobManager) 36 | { 37 | var jobId = "HelloWorld"; 38 | recurringJobManager.AddOrUpdate(jobId, (x) => x.HelloWorld(), Cron.Hourly); 39 | return Ok(jobId); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/DynamoController.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Services.AWS; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System.Threading.Tasks; 4 | 5 | // For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 6 | 7 | namespace BervProject.WebApi.Boilerplate.Controllers 8 | { 9 | [ApiController] 10 | [Route("api/v{version:apiVersion}/[controller]")] 11 | [ApiVersion("1.0")] 12 | public class DynamoController : ControllerBase 13 | { 14 | private readonly IDynamoDbServices _dynamoService; 15 | public DynamoController(IDynamoDbServices dynamoService) 16 | { 17 | _dynamoService = dynamoService; 18 | } 19 | 20 | /// 21 | /// Create Dynamo DB Object 22 | /// 23 | /// 24 | [HttpPost] 25 | public async Task Create() 26 | { 27 | await _dynamoService.CreateObject(); 28 | return Ok(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/EmailController.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Models; 2 | using BervProject.WebApi.Boilerplate.Services.AWS; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Threading.Tasks; 5 | 6 | namespace BervProject.WebApi.Boilerplate.Controllers 7 | { 8 | [ApiController] 9 | [Route("api/v{version:apiVersion}/[controller]")] 10 | [ApiVersion("1.0")] 11 | public class EmailController : ControllerBase 12 | { 13 | private readonly IEmailService _emailService; 14 | public EmailController(IEmailService emailService) 15 | { 16 | _emailService = emailService; 17 | } 18 | 19 | /// 20 | /// Send Email 21 | /// 22 | /// 23 | /// 24 | [HttpPost("send")] 25 | public async Task SendEmail([FromBody] EmailSendRequest request) 26 | { 27 | await _emailService.SendEmail(request.To); 28 | return Ok(new 29 | { 30 | Status = 200 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/ErrorController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Controllers 4 | { 5 | [ApiController] 6 | [Route("api/v{version:apiVersion}/[controller]")] 7 | [ApiVersion("1.0")] 8 | public class ErrorController : ControllerBase 9 | { 10 | /// 11 | /// Error Controller 12 | /// 13 | /// 14 | [Route("error")] 15 | [HttpGet] 16 | public IActionResult Error() => Problem(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/NoteController.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Entities; 2 | using BervProject.WebApi.Boilerplate.Services; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace BervProject.WebApi.Boilerplate.Controllers 8 | { 9 | [ApiController] 10 | [Route("api/v{version:apiVersion}/[controller]")] 11 | [ApiVersion("1.0")] 12 | public class NoteController : ControllerBase 13 | { 14 | private readonly IAzureTableStorageService _noteTable; 15 | public NoteController(IAzureTableStorageService noteTable) 16 | { 17 | _noteTable = noteTable; 18 | } 19 | 20 | /// 21 | /// Create Azure Table Storage 22 | /// 23 | /// 24 | [HttpPost("createTable")] 25 | [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] 26 | 27 | public async Task CreateTable() 28 | { 29 | await _noteTable.CreateTableAsync(); 30 | return Ok(true); 31 | } 32 | 33 | /// 34 | /// Upsert Note 35 | /// 36 | /// 37 | /// 38 | [HttpPost("upsert")] 39 | [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] 40 | public async Task UpsertNote([FromBody] Note note) 41 | { 42 | await _noteTable.UpsertAsync(note); 43 | return Ok(true); 44 | } 45 | 46 | /// 47 | /// Get Note 48 | /// 49 | /// 50 | /// 51 | /// 52 | [HttpGet("get")] 53 | [ProducesResponseType(typeof(Note), StatusCodes.Status200OK)] 54 | public async Task Get([FromQuery] string partitionKey, [FromQuery] string rowKey) 55 | { 56 | var result = await _noteTable.GetAsync(partitionKey, rowKey); 57 | return Ok(result); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/ServiceBusSenderController.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Models; 2 | using BervProject.WebApi.Boilerplate.Models.Response; 3 | using BervProject.WebApi.Boilerplate.Services.Azure; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | 8 | namespace BervProject.WebApi.Boilerplate.Controllers 9 | { 10 | [Route("api/v{version:apiVersion}/[controller]")] 11 | [ApiController] 12 | [ApiVersion("1.0")] 13 | public class ServiceBusSenderController : ControllerBase 14 | { 15 | 16 | /// 17 | /// Send Service Bus Message 18 | /// 19 | /// 20 | /// 21 | /// 22 | [HttpPost("sendMessage")] 23 | [ProducesResponseType(typeof(MessageSenderResponse), StatusCodes.Status200OK)] 24 | public async Task> SendMessage([FromServices] IAzureQueueServices queueServices, [FromBody] MessageData messageData) 25 | { 26 | var response = new MessageSenderResponse() 27 | { 28 | YourMessage = messageData.Message 29 | }; 30 | var result = await queueServices.SendMessage(messageData.Message); 31 | response.IsSuccess = result; 32 | return response; 33 | } 34 | 35 | /// 36 | /// Send Topic Message 37 | /// 38 | /// 39 | /// 40 | /// 41 | [HttpPost("sendTopic")] 42 | [ProducesResponseType(typeof(MessageSenderResponse), StatusCodes.Status200OK)] 43 | public async Task> SendTopic([FromServices] ITopicServices topicServices, [FromBody] MessageData messageData) 44 | { 45 | var response = new MessageSenderResponse() 46 | { 47 | YourMessage = messageData.Message 48 | }; 49 | var result = await topicServices.SendTopic(messageData.Message); 50 | response.IsSuccess = result; 51 | return response; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/StorageQueueController.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Models; 2 | using BervProject.WebApi.Boilerplate.Models.Response; 3 | using BervProject.WebApi.Boilerplate.Services.Azure; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace BervProject.WebApi.Boilerplate.Controllers 8 | { 9 | [ApiController] 10 | [Route("api/v{version:apiVersion}/[controller]")] 11 | [ApiVersion("1.0")] 12 | public class StorageQueueController : ControllerBase 13 | { 14 | private readonly IAzureStorageQueueService _azureStorageQueueService; 15 | public StorageQueueController(IAzureStorageQueueService azureStorageQueueService) 16 | { 17 | _azureStorageQueueService = azureStorageQueueService; 18 | } 19 | 20 | /// 21 | /// Send Queue Message 22 | /// 23 | /// 24 | /// 25 | [HttpPost("sendMessage")] 26 | [ProducesResponseType(typeof(MessageSenderResponse), StatusCodes.Status200OK)] 27 | public ActionResult SendMessage([FromBody] MessageData messageData) 28 | { 29 | var response = new MessageSenderResponse() 30 | { 31 | YourMessage = messageData.Message 32 | }; 33 | var result = _azureStorageQueueService.SendMessage(messageData.Message); 34 | response.IsSuccess = result; 35 | return response; 36 | } 37 | 38 | /// 39 | /// Getting Latest Message 40 | /// 41 | /// 42 | [HttpGet("receiveMessage")] 43 | [ProducesResponseType(typeof(MessageSenderResponse), StatusCodes.Status200OK)] 44 | public ActionResult GetLatestMessage() 45 | { 46 | var response = new MessageSenderResponse(); 47 | var result = _azureStorageQueueService.ReceiveMessage(); 48 | response.IsSuccess = result != null; 49 | response.YourMessage = result; 50 | return response; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Entities; 2 | using BervProject.WebApi.Boilerplate.EntityFramework; 3 | using BervProject.WebApi.Boilerplate.Models; 4 | using Microsoft.ApplicationInsights; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.EntityFrameworkCore; 7 | using Microsoft.Extensions.Caching.Distributed; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using Microsoft.AspNetCore.Http; 13 | 14 | namespace BervProject.WebApi.Boilerplate.Controllers 15 | { 16 | [ApiController] 17 | [Route("api/v{version:apiVersion}/[controller]")] 18 | [ApiVersion("1.0")] 19 | public class WeatherForecastController : ControllerBase 20 | { 21 | private static readonly string[] Summaries = new[] 22 | { 23 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 24 | }; 25 | 26 | private readonly TelemetryClient telemetryClient; 27 | 28 | public WeatherForecastController(TelemetryClient telemetryClient) 29 | { 30 | this.telemetryClient = telemetryClient; 31 | } 32 | 33 | /// 34 | /// Get Weather Forecast 35 | /// 36 | /// 37 | [HttpGet] 38 | [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] 39 | public IEnumerable Get() 40 | { 41 | this.telemetryClient.TrackEvent("WeatherQueried"); 42 | var rng = new Random(); 43 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast 44 | { 45 | Date = DateTime.Now.AddDays(index), 46 | TemperatureC = rng.Next(-20, 55), 47 | Summary = Summaries[rng.Next(Summaries.Length)] 48 | }) 49 | .ToArray(); 50 | } 51 | 52 | /// 53 | /// Get Cache 54 | /// 55 | /// 56 | /// 57 | [HttpGet("cache")] 58 | [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] 59 | public ActionResult GetCache([FromServices] IDistributedCache distributedCache) 60 | { 61 | var result = distributedCache.Get("MyCache"); 62 | if (result == null || result.Length == 0) 63 | { 64 | var utf8 = new UTF8Encoding(); 65 | distributedCache.Set("MyCache", utf8.GetBytes("CustomCache")); 66 | result = distributedCache.Get("MyCache"); 67 | Console.WriteLine(result); 68 | } 69 | return Encoding.UTF8.GetString(result ?? Array.Empty()); 70 | } 71 | 72 | /// 73 | /// Simple DB Query 74 | /// 75 | /// 76 | /// 77 | [HttpGet("db")] 78 | [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] 79 | public ActionResult> GetDb([FromServices] BoilerplateDbContext dbContext) 80 | { 81 | var booksQuery = dbContext.Books.AsQueryable(); 82 | var books = booksQuery.Where(x => x.Name.Contains("Halleluya")).Include(x => x.Publisher).ToList(); 83 | if (books.Count == 0) 84 | { 85 | var listBooks = new List() 86 | { 87 | new() 88 | { 89 | Id = Guid.NewGuid(), 90 | Name = "Halleluya", 91 | Publisher = new Publisher() 92 | { 93 | Id = Guid.NewGuid(), 94 | Name = "Heaven Publisher" 95 | } 96 | }, 97 | new() 98 | { 99 | Id = Guid.NewGuid(), 100 | Name = "Halleluya 2", 101 | Publisher = new Publisher() 102 | { 103 | Id = Guid.NewGuid(), 104 | Name = "Heaven Publisher" 105 | } 106 | } 107 | }; 108 | dbContext.Books.AddRange(listBooks); 109 | dbContext.SaveChanges(); 110 | return Ok(listBooks); 111 | } 112 | 113 | return Ok(books); 114 | } 115 | 116 | /// 117 | /// Exception always throw 118 | /// 119 | /// 120 | /// 121 | [HttpGet("triggerException")] 122 | [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] 123 | public IActionResult TriggerException() 124 | { 125 | throw new Exception("Unhandled Exception"); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Entities/Book.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace BervProject.WebApi.Boilerplate.Entities 5 | { 6 | public class Book 7 | { 8 | public Guid Id { get; set; } 9 | [Required] 10 | public string Name { get; set; } 11 | public virtual Publisher Publisher { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Entities/Note.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using Azure; 4 | using Azure.Data.Tables; 5 | 6 | namespace BervProject.WebApi.Boilerplate.Entities 7 | { 8 | public class Note : ITableEntity 9 | { 10 | public string PartitionKey { get; set; } 11 | public string RowKey { get; set; } 12 | public string Title { get; set; } 13 | public string Message { get; set; } 14 | public DateTimeOffset? Timestamp { get; set; } 15 | public ETag ETag { get; set; } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Entities/Publisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace BervProject.WebApi.Boilerplate.Entities 7 | { 8 | public class Publisher 9 | { 10 | public Guid Id { get; set; } 11 | [Required] 12 | public string Name { get; set; } 13 | [JsonIgnore] 14 | public virtual ICollection Books { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/EntityFramework/BoilerplateDbContext.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Boilerplate.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace BervProject.WebApi.Boilerplate.EntityFramework 5 | { 6 | public class BoilerplateDbContext : DbContext 7 | { 8 | public DbSet Books { get; set; } 9 | public DbSet Publishers { get; set; } 10 | 11 | public BoilerplateDbContext() : base() 12 | { 13 | 14 | } 15 | 16 | public BoilerplateDbContext(DbContextOptions options) : base(options) 17 | { 18 | 19 | } 20 | 21 | protected override void OnModelCreating(ModelBuilder modelBuilder) 22 | { 23 | base.OnModelCreating(modelBuilder); 24 | modelBuilder.Entity().HasOne(m => m.Publisher).WithMany(m => m.Books); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Extensions/SetupAWSExtension.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.Extenstions; 2 | using Amazon.DynamoDBv2; 3 | using Amazon.S3; 4 | using Amazon.SimpleEmail; 5 | using BervProject.WebApi.Boilerplate.Services.AWS; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | public static class SetupAWSExtension 9 | { 10 | public static void SetupAWS(this IServiceCollection services) 11 | { 12 | services.AddAWSService(); 13 | services.AddAWSService(); 14 | services.AddAWSService(); 15 | services.AddScoped(); 16 | services.AddScoped(); 17 | services.AddScoped(); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Extensions/SetupAzureExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Extenstions; 4 | 5 | using Entities; 6 | using BervProject.WebApi.Boilerplate.Services.Azure; 7 | using Services; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Azure; 10 | 11 | 12 | public static class SetupAzureExtension 13 | { 14 | public static void SetupAzure(this IServiceCollection services, ConfigurationManager config) 15 | { 16 | services.AddAzureClients(builder => 17 | { 18 | builder.AddBlobServiceClient(config.GetConnectionString("AzureStorageBlob")); 19 | builder.AddQueueServiceClient(config.GetConnectionString("AzureStorageQueue")); 20 | builder.AddServiceBusClient(config.GetConnectionString("AzureServiceBus")); 21 | builder.AddTableServiceClient(config.GetConnectionString("AzureStorageTable")); 22 | }); 23 | services.AddScoped(); 24 | services.AddScoped(); 25 | services.AddScoped(); 26 | services.AddScoped(); 27 | // add each tables 28 | services.AddScoped, AzureTableStorageService>(); 29 | // service bus 30 | services.AddSingleton(); 31 | services.AddSingleton(); 32 | services.AddTransient(); 33 | services.AddApplicationInsightsTelemetry(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Migrations/20210118042709_NewBookDb.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using BervProject.WebApi.Boilerplate.EntityFramework; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; 9 | 10 | namespace BervProject.WebApi.Boilerplate.Migrations 11 | { 12 | [DbContext(typeof(BoilerplateDbContext))] 13 | [Migration("20210118042709_NewBookDb")] 14 | partial class NewBookDb 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .UseIdentityByDefaultColumns() 21 | .HasAnnotation("Relational:MaxIdentifierLength", 63) 22 | .HasAnnotation("ProductVersion", "5.0.2"); 23 | 24 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Book", b => 25 | { 26 | b.Property("Id") 27 | .ValueGeneratedOnAdd() 28 | .HasColumnType("uuid"); 29 | 30 | b.Property("Name") 31 | .IsRequired() 32 | .HasColumnType("text"); 33 | 34 | b.Property("PublisherId") 35 | .HasColumnType("uuid"); 36 | 37 | b.HasKey("Id"); 38 | 39 | b.HasIndex("PublisherId"); 40 | 41 | b.ToTable("Books"); 42 | }); 43 | 44 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Publisher", b => 45 | { 46 | b.Property("Id") 47 | .ValueGeneratedOnAdd() 48 | .HasColumnType("uuid"); 49 | 50 | b.Property("Name") 51 | .IsRequired() 52 | .HasColumnType("text"); 53 | 54 | b.HasKey("Id"); 55 | 56 | b.ToTable("Publishers"); 57 | }); 58 | 59 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Book", b => 60 | { 61 | b.HasOne("BervProject.WebApi.Boilerplate.Entities.Publisher", "Publisher") 62 | .WithMany("Books") 63 | .HasForeignKey("PublisherId"); 64 | 65 | b.Navigation("Publisher"); 66 | }); 67 | 68 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Publisher", b => 69 | { 70 | b.Navigation("Books"); 71 | }); 72 | #pragma warning restore 612, 618 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Migrations/20210118042709_NewBookDb.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | using System; 3 | 4 | namespace BervProject.WebApi.Boilerplate.Migrations 5 | { 6 | public partial class NewBookDb : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "Publishers", 12 | columns: table => new 13 | { 14 | Id = table.Column(type: "uuid", nullable: false), 15 | Name = table.Column(type: "text", nullable: false) 16 | }, 17 | constraints: table => 18 | { 19 | table.PrimaryKey("PK_Publishers", x => x.Id); 20 | }); 21 | 22 | migrationBuilder.CreateTable( 23 | name: "Books", 24 | columns: table => new 25 | { 26 | Id = table.Column(type: "uuid", nullable: false), 27 | Name = table.Column(type: "text", nullable: false), 28 | PublisherId = table.Column(type: "uuid", nullable: true) 29 | }, 30 | constraints: table => 31 | { 32 | table.PrimaryKey("PK_Books", x => x.Id); 33 | table.ForeignKey( 34 | name: "FK_Books_Publishers_PublisherId", 35 | column: x => x.PublisherId, 36 | principalTable: "Publishers", 37 | principalColumn: "Id", 38 | onDelete: ReferentialAction.Restrict); 39 | }); 40 | 41 | migrationBuilder.CreateIndex( 42 | name: "IX_Books_PublisherId", 43 | table: "Books", 44 | column: "PublisherId"); 45 | } 46 | 47 | protected override void Down(MigrationBuilder migrationBuilder) 48 | { 49 | migrationBuilder.DropTable( 50 | name: "Books"); 51 | 52 | migrationBuilder.DropTable( 53 | name: "Publishers"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Migrations/BoilerplateDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using BervProject.WebApi.Boilerplate.EntityFramework; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; 8 | 9 | namespace BervProject.WebApi.Boilerplate.Migrations 10 | { 11 | [DbContext(typeof(BoilerplateDbContext))] 12 | partial class BoilerplateDbContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .UseIdentityByDefaultColumns() 19 | .HasAnnotation("Relational:MaxIdentifierLength", 63) 20 | .HasAnnotation("ProductVersion", "5.0.2"); 21 | 22 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Book", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd() 26 | .HasColumnType("uuid"); 27 | 28 | b.Property("Name") 29 | .IsRequired() 30 | .HasColumnType("text"); 31 | 32 | b.Property("PublisherId") 33 | .HasColumnType("uuid"); 34 | 35 | b.HasKey("Id"); 36 | 37 | b.HasIndex("PublisherId"); 38 | 39 | b.ToTable("Books"); 40 | }); 41 | 42 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Publisher", b => 43 | { 44 | b.Property("Id") 45 | .ValueGeneratedOnAdd() 46 | .HasColumnType("uuid"); 47 | 48 | b.Property("Name") 49 | .IsRequired() 50 | .HasColumnType("text"); 51 | 52 | b.HasKey("Id"); 53 | 54 | b.ToTable("Publishers"); 55 | }); 56 | 57 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Book", b => 58 | { 59 | b.HasOne("BervProject.WebApi.Boilerplate.Entities.Publisher", "Publisher") 60 | .WithMany("Books") 61 | .HasForeignKey("PublisherId"); 62 | 63 | b.Navigation("Publisher"); 64 | }); 65 | 66 | modelBuilder.Entity("BervProject.WebApi.Boilerplate.Entities.Publisher", b => 67 | { 68 | b.Navigation("Books"); 69 | }); 70 | #pragma warning restore 612, 618 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Models/EmailSendRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace BervProject.WebApi.Boilerplate.Models 5 | { 6 | public class EmailSendRequest 7 | { 8 | [Required] 9 | public List To { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Models/MessageData.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.Models 2 | { 3 | public class MessageData 4 | { 5 | public string Message { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Models/Request/UploadFile.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Newtonsoft.Json; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace BervProject.WebApi.Boilerplate.Models.Request 6 | { 7 | public class UploadFile 8 | { 9 | [Required] 10 | public IFormFile File { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Models/Response/MessageSenderResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.Models.Response 2 | { 3 | public class MessageSenderResponse 4 | { 5 | public bool IsSuccess { get; set; } 6 | public string YourMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Models/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Models 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/NLog.config: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using Autofac.Extensions.DependencyInjection; 5 | using BervProject.WebApi.Boilerplate.ConfigModel; 6 | using BervProject.WebApi.Boilerplate.EntityFramework; 7 | using BervProject.WebApi.Boilerplate.Extenstions; 8 | using BervProject.WebApi.Boilerplate.Services; 9 | using BervProject.WebApi.Boilerplate.Services.Azure; 10 | using Hangfire; 11 | using Hangfire.PostgreSql; 12 | using Microsoft.AspNetCore.Builder; 13 | using Microsoft.EntityFrameworkCore; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Hosting; 17 | using Microsoft.Extensions.Logging; 18 | using Microsoft.OpenApi.Models; 19 | using NLog.Web; 20 | 21 | var builder = WebApplication.CreateBuilder(args); 22 | builder.AddServiceDefaults(); 23 | builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); 24 | builder.Logging.ClearProviders(); 25 | builder.Logging.SetMinimumLevel(LogLevel.Trace); 26 | builder.Logging.AddNLog("Nlog.config"); 27 | builder.Logging.AddNLogWeb(); 28 | builder.Host.UseNLog(); 29 | 30 | // settings injection 31 | var awsConfig = builder.Configuration.GetSection("AWS").Get(); 32 | builder.Services.AddSingleton(awsConfig); 33 | 34 | var azureConfig = builder.Configuration.GetSection("Azure").Get(); 35 | builder.Services.AddSingleton(azureConfig); 36 | 37 | // aws services 38 | builder.Services.SetupAWS(); 39 | 40 | // azure services 41 | builder.Services.SetupAzure(builder.Configuration); 42 | 43 | // cron services 44 | builder.Services.AddScoped(); 45 | builder.Services.AddHangfire(x => x.UsePostgreSqlStorage(opt => 46 | { 47 | opt.UseNpgsqlConnection(builder.Configuration.GetConnectionString("BoilerplateConnectionString")); 48 | })); 49 | builder.Services.AddHangfireServer(); 50 | 51 | // essential services 52 | builder.Services.AddStackExchangeRedisCache(options => 53 | { 54 | options.Configuration = builder.Configuration.GetConnectionString("Redis"); 55 | }); 56 | builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("BoilerplateConnectionString"))); 57 | 58 | builder.Services.AddControllers(); 59 | builder.Services.AddApiVersioning(); 60 | builder.Services.AddSwaggerGen(options => 61 | { 62 | var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; 63 | options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); 64 | options.SwaggerDoc("v1", new OpenApiInfo 65 | { 66 | Version = "v1", 67 | Title = "Boilerplate API", 68 | Description = "An ASP.NET Core Web API" 69 | }); 70 | }); 71 | 72 | var app = builder.Build(); 73 | 74 | // register Consumer 75 | var connectionString = builder.Configuration.GetConnectionString("AzureServiceBus"); 76 | var queueName = azureConfig.ServiceBus.QueueName; 77 | var topicName = azureConfig.ServiceBus.TopicName; 78 | if (!string.IsNullOrWhiteSpace(queueName) && !string.IsNullOrWhiteSpace(connectionString)) 79 | { 80 | var bus = app.Services.GetService(); 81 | bus.RegisterOnMessageHandlerAndReceiveMessages(); 82 | } 83 | if (!string.IsNullOrWhiteSpace(topicName) && !string.IsNullOrWhiteSpace(connectionString)) 84 | { 85 | var bus = app.Services.GetService(); 86 | bus.RegisterOnMessageHandlerAndReceiveMessages(); 87 | } 88 | 89 | // register essential things 90 | if (app.Environment.IsDevelopment()) 91 | { 92 | app.UseDeveloperExceptionPage(); 93 | } 94 | else 95 | { 96 | app.UseExceptionHandler("/error"); 97 | app.UseHttpsRedirection(); 98 | } 99 | 100 | app.UseRouting(); 101 | 102 | app.UseAuthorization(); 103 | 104 | app.UseSwagger(c => 105 | { 106 | c.RouteTemplate = "api/docs/{documentName}/swagger.json"; 107 | }); 108 | 109 | app.UseSwaggerUI(c => 110 | { 111 | c.SwaggerEndpoint("/api/docs/v1/swagger.json", "My API V1"); 112 | c.RoutePrefix = "api/docs"; 113 | }); 114 | 115 | app.MapDefaultEndpoints(); 116 | 117 | app.MapControllers(); 118 | app.MapHangfireDashboard(); 119 | 120 | app.Run(); 121 | 122 | public partial class Program { } 123 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/AWS/AWSS3Service.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | using Amazon.S3; 4 | using Amazon.S3.Model; 5 | 6 | namespace BervProject.WebApi.Boilerplate.Services.AWS 7 | { 8 | public class AWSS3Service : IAWSS3Service 9 | { 10 | private readonly IAmazonS3 _s3Client; 11 | public AWSS3Service(IAmazonS3 amazonS3) 12 | { 13 | _s3Client = amazonS3; 14 | } 15 | public async Task UploadFile(IFormFile formFile) 16 | { 17 | var location = $"uploads/{formFile.FileName}"; 18 | using var stream = formFile.OpenReadStream(); 19 | var putRequest = new PutObjectRequest 20 | { 21 | Key = location, 22 | BucketName = "upload-test-berv", 23 | InputStream = stream, 24 | AutoCloseStream = true, 25 | ContentType = formFile.ContentType 26 | }; 27 | await _s3Client.PutObjectAsync(putRequest); 28 | return location; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/AWS/DynamoDbServices.cs: -------------------------------------------------------------------------------- 1 | using Amazon.DynamoDBv2; 2 | using Amazon.DynamoDBv2.Model; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text.Json; 7 | using System.Threading.Tasks; 8 | 9 | namespace BervProject.WebApi.Boilerplate.Services.AWS 10 | { 11 | public class DynamoDbServices : IDynamoDbServices 12 | { 13 | private readonly IAmazonDynamoDB _dynamoClient; 14 | private readonly ILogger _logger; 15 | public DynamoDbServices(IAmazonDynamoDB amazonDynamoDb, ILogger logger) 16 | { 17 | _logger = logger; 18 | _dynamoClient = amazonDynamoDb; 19 | } 20 | 21 | public async Task CreateObject() 22 | { 23 | var request = new PutItemRequest() 24 | { 25 | TableName = "dev-test", 26 | Item = new Dictionary 27 | { 28 | { "Id", new AttributeValue 29 | { 30 | S = Guid.NewGuid().ToString() 31 | }}, 32 | { "Name", new AttributeValue 33 | { 34 | S = "Hello World!" 35 | }} 36 | } 37 | }; 38 | var response = await _dynamoClient.PutItemAsync(request); 39 | string message = $"Response: {response.HttpStatusCode}, {JsonSerializer.Serialize(response.Attributes)}"; 40 | _logger.LogInformation(message); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/AWS/EmailService.cs: -------------------------------------------------------------------------------- 1 | using Amazon.SimpleEmail; 2 | using Amazon.SimpleEmail.Model; 3 | using Microsoft.Extensions.Logging; 4 | using System.Collections.Generic; 5 | using System.Text.Json; 6 | using System.Text.Json.Serialization; 7 | using System.Threading.Tasks; 8 | 9 | namespace BervProject.WebApi.Boilerplate.Services.AWS 10 | { 11 | public class EmailService : IEmailService 12 | { 13 | private readonly IAmazonSimpleEmailService _emailClient; 14 | private readonly ILogger _logger; 15 | public EmailService(ILogger logger, IAmazonSimpleEmailService emailClient) 16 | { 17 | _logger = logger; 18 | _emailClient = emailClient; 19 | } 20 | 21 | public async Task SendEmail(List receiver) 22 | { 23 | var request = new SendEmailRequest() 24 | { 25 | ReplyToAddresses = new List { "bervianto.leo@gmail.com" }, 26 | Message = new Message() 27 | { 28 | Body = new Body(new Content("Hello World!")), 29 | Subject = new Content("Stand by me") 30 | }, 31 | Destination = new Destination(receiver), 32 | Source = "support@berviantoleo.my.id" 33 | }; 34 | var response = await _emailClient.SendEmailAsync(request); 35 | string messageId = $"Message id: {response.MessageId}"; 36 | _logger.LogDebug(messageId); 37 | if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) 38 | { 39 | _logger.LogInformation("Finished Sent Email"); 40 | } 41 | else 42 | { 43 | _logger.LogWarning("There is a problem when sending email"); 44 | string message = $"Error: {response.MessageId}:{response.HttpStatusCode}:{JsonSerializer.Serialize(response.ResponseMetadata.Metadata)}"; 45 | _logger.LogWarning(message); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/AWS/IAWSS3Service.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace BervProject.WebApi.Boilerplate.Services.AWS 5 | { 6 | public interface IAWSS3Service 7 | { 8 | Task UploadFile(IFormFile formFile); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/AWS/IDynamoDbServices.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Services.AWS 4 | { 5 | public interface IDynamoDbServices 6 | { 7 | Task CreateObject(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/AWS/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace BervProject.WebApi.Boilerplate.Services.AWS 5 | { 6 | public interface IEmailService 7 | { 8 | Task SendEmail(List receiver); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/AzureQueueServices.cs: -------------------------------------------------------------------------------- 1 | using Azure.Messaging.ServiceBus; 2 | using BervProject.WebApi.Boilerplate.ConfigModel; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace BervProject.WebApi.Boilerplate.Services.Azure 8 | { 9 | public class AzureQueueServices : IAzureQueueServices 10 | { 11 | private readonly string _queueName; 12 | private readonly ServiceBusSender _serviceBusSender; 13 | private readonly ILogger _logger; 14 | public AzureQueueServices(AzureConfiguration azureConfiguration, ILogger logger, ServiceBusClient serviceBusClient) 15 | { 16 | _logger = logger; 17 | _queueName = azureConfiguration.ServiceBus.QueueName; 18 | _serviceBusSender = serviceBusClient.CreateSender(_queueName); 19 | } 20 | 21 | public async Task SendMessage(string message) 22 | { 23 | try 24 | { 25 | var messageQueue = new ServiceBusMessage(message); 26 | _logger.LogDebug($"Sending message: {message}"); 27 | await _serviceBusSender.SendMessageAsync(messageQueue); 28 | _logger.LogDebug($"Sent message: {message}"); 29 | return true; 30 | } 31 | catch (Exception ex) 32 | { 33 | _logger.LogError(ex.ToString()); 34 | return false; 35 | } 36 | finally 37 | { 38 | await _serviceBusSender.CloseAsync(); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/AzureStorageQueueService.cs: -------------------------------------------------------------------------------- 1 | using Azure.Storage.Queues; 2 | using BervProject.WebApi.Boilerplate.ConfigModel; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | 6 | namespace BervProject.WebApi.Boilerplate.Services.Azure 7 | { 8 | public class AzureStorageQueueService : IAzureStorageQueueService 9 | { 10 | private readonly ILogger _logger; 11 | private readonly QueueClient _queueClient; 12 | private readonly string _queueName; 13 | 14 | public AzureStorageQueueService(ILogger logger, 15 | AzureConfiguration azureConfiguration, 16 | QueueServiceClient queueServiceClient) 17 | { 18 | _logger = logger; 19 | _queueName = azureConfiguration.Storage.Queue.QueueName; 20 | _queueClient = queueServiceClient.GetQueueClient(_queueName); 21 | _queueClient.CreateIfNotExists(); 22 | } 23 | 24 | public string ReceiveMessage() 25 | { 26 | try 27 | { 28 | if (_queueClient.Exists()) 29 | { 30 | var response = _queueClient.ReceiveMessage(); 31 | var message = response?.Value; 32 | if (message != null) 33 | { 34 | var textMessage = message.Body.ToString(); 35 | _logger.LogDebug($"Get message from {_queueName}:{message.MessageId}: {textMessage}"); 36 | _logger.LogDebug($"Message {message.MessageId} deqeue from {_queueName}"); 37 | var responseDelete = _queueClient.DeleteMessage(message.MessageId, message.PopReceipt); 38 | _logger.LogDebug($"Message finished deqeue from {_queueName}: {responseDelete.ClientRequestId}"); 39 | return textMessage; 40 | } 41 | else 42 | { 43 | _logger.LogDebug($"Empty message at {_queueName}"); 44 | return null; 45 | } 46 | } 47 | else 48 | { 49 | _logger.LogWarning($"{_queueName} is not exists"); 50 | return null; 51 | } 52 | } 53 | catch (Exception ex) 54 | { 55 | _logger.LogError(ex, "Something Error, ignoring"); 56 | return null; 57 | } 58 | } 59 | 60 | public bool SendMessage(string message) 61 | { 62 | try 63 | { 64 | if (_queueClient.Exists()) 65 | { 66 | _logger.LogDebug($"Sending message: {message} at {_queueName}"); 67 | var response = _queueClient.SendMessage(message); 68 | var messageId = response?.Value?.MessageId; 69 | _logger.LogDebug($"Sent message to {_queueName} with id: {messageId}"); 70 | return true; 71 | } 72 | else 73 | { 74 | _logger.LogWarning($"{_queueName} is not exists"); 75 | return false; 76 | } 77 | } 78 | catch (Exception ex) 79 | { 80 | _logger.LogError(ex.ToString()); 81 | return false; 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/AzureTableStorageService.cs: -------------------------------------------------------------------------------- 1 | 2 | using Azure.Data.Tables; 3 | using BervProject.WebApi.Boilerplate.ConfigModel; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.Extensions.Logging; 6 | using System.Threading.Tasks; 7 | 8 | namespace BervProject.WebApi.Boilerplate.Services.Azure 9 | { 10 | public class AzureTableStorageService : IAzureTableStorageService 11 | where T : class, ITableEntity, new() 12 | { 13 | private readonly ILogger> _logger; 14 | private readonly TableServiceClient _tableServiceClient; 15 | private readonly string _tableName; 16 | public AzureTableStorageService(ILogger> logger, 17 | AzureConfiguration azureConfiguration, 18 | TableServiceClient tableServiceClient) 19 | { 20 | _logger = logger; 21 | _tableServiceClient = tableServiceClient; 22 | _tableName = typeof(T).ShortDisplayName(); 23 | } 24 | 25 | public async Task CreateTableAsync() 26 | { 27 | _logger.LogInformation($"Creating table: {_tableName}"); 28 | await _tableServiceClient.CreateTableIfNotExistsAsync(_tableName); 29 | _logger.LogInformation($"{_tableName} created"); 30 | } 31 | 32 | public async Task UpsertAsync(T data) 33 | { 34 | var tableClient = _tableServiceClient.GetTableClient(_tableName); 35 | var response = await tableClient.UpsertEntityAsync(data); 36 | if (response.IsError) 37 | { 38 | _logger.LogError(response.ReasonPhrase); 39 | } 40 | } 41 | 42 | public async Task GetAsync(string partitionKey, string rowKey) 43 | { 44 | var tableClient = _tableServiceClient.GetTableClient(_tableName); 45 | var response = await tableClient.GetEntityAsync(partitionKey, rowKey); 46 | return response.Value; 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/BlobService.cs: -------------------------------------------------------------------------------- 1 | using Azure.Storage.Blobs; 2 | using BervProject.WebApi.Boilerplate.ConfigModel; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.Logging; 5 | using System.Collections.Generic; 6 | 7 | namespace BervProject.WebApi.Boilerplate.Services.Azure 8 | { 9 | public class BlobService : IBlobService 10 | { 11 | private readonly ILogger _logger; 12 | private readonly BlobContainerClient _blobContainerClient; 13 | private readonly string _containerName; 14 | public BlobService(ILogger logger, AzureConfiguration azureConfiguration, BlobServiceClient blobServiceClient) 15 | { 16 | _logger = logger; 17 | _containerName = azureConfiguration.Storage.Blob.ContainerName; 18 | _blobContainerClient = blobServiceClient.GetBlobContainerClient(_containerName); 19 | } 20 | 21 | public void CreateStorageContainer() 22 | { 23 | _logger.LogDebug($"Create Blob Container {_containerName}"); 24 | _blobContainerClient.CreateIfNotExists(); 25 | _logger.LogDebug($"Blob Container {_containerName} created"); 26 | } 27 | 28 | public List> GetBlobsInfo() 29 | { 30 | var list = new List>(); 31 | if (_blobContainerClient.Exists()) 32 | { 33 | var blobs = _blobContainerClient.GetBlobs(); 34 | foreach (var blob in blobs) 35 | { 36 | _logger.LogDebug($"{blob.Name} --> Created On: {blob.Properties.CreatedOn:YYYY-MM-dd HH:mm:ss} Size: {blob.Properties.ContentLength}"); 37 | list.Add(new Dictionary 38 | { 39 | { "name", blob.Name }, 40 | { "createdDate", blob.Properties.CreatedOn?.ToString() }, 41 | { "size", blob.Properties.ContentLength?.ToString() }, 42 | { "version", blob.VersionId }, 43 | { "deleted", blob.Deleted.ToString() } 44 | }); 45 | } 46 | } 47 | else 48 | { 49 | _logger.LogWarning($"Can't get data, container {_containerName} not created yet"); 50 | } 51 | return list; 52 | } 53 | 54 | public void UploadFile(IFormFile formFile) 55 | { 56 | if (_blobContainerClient.Exists()) 57 | { 58 | var fileName = formFile.FileName; 59 | BlobClient blobClient = _blobContainerClient.GetBlobClient(fileName); 60 | using var stream = formFile.OpenReadStream(); 61 | _logger.LogDebug($"Uploading {fileName}"); 62 | blobClient.Upload(stream, true); 63 | _logger.LogDebug($"{fileName} uploaded"); 64 | } 65 | else 66 | { 67 | _logger.LogWarning($"Can't upload, container {_containerName} not created yet"); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/IAzureQueueServices.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Services.Azure 4 | { 5 | public interface IAzureQueueServices 6 | { 7 | Task SendMessage(string message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/IAzureStorageQueueService.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.Services.Azure 2 | { 3 | public interface IAzureStorageQueueService 4 | { 5 | bool SendMessage(string message); 6 | string ReceiveMessage(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/IAzureTableStorageService.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.Services; 2 | 3 | using System.Threading.Tasks; 4 | 5 | public interface IAzureTableStorageService 6 | { 7 | Task CreateTableAsync(); 8 | Task UpsertAsync(T data); 9 | Task GetAsync(string partitionKey, string rowKey); 10 | } -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/IBlobService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System.Collections.Generic; 3 | 4 | namespace BervProject.WebApi.Boilerplate.Services.Azure 5 | { 6 | public interface IBlobService 7 | { 8 | void CreateStorageContainer(); 9 | List> GetBlobsInfo(); 10 | void UploadFile(IFormFile formFile); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/IServiceBusQueueConsumer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Services.Azure 4 | { 5 | public interface IServiceBusQueueConsumer 6 | { 7 | void RegisterOnMessageHandlerAndReceiveMessages(); 8 | Task CloseQueueAsync(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/IServiceBusTopicSubscription.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Services.Azure 4 | { 5 | public interface IServiceBusTopicSubscription 6 | { 7 | void RegisterOnMessageHandlerAndReceiveMessages(); 8 | Task CloseSubscriptionClientAsync(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/ITopicServices.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Services.Azure 4 | { 5 | public interface ITopicServices 6 | { 7 | Task SendTopic(string message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/ServiceBusQueueConsumer.cs: -------------------------------------------------------------------------------- 1 | using Azure.Messaging.ServiceBus; 2 | using BervProject.WebApi.Boilerplate.ConfigModel; 3 | using Microsoft.Extensions.Logging; 4 | using System.Threading.Tasks; 5 | 6 | namespace BervProject.WebApi.Boilerplate.Services.Azure 7 | { 8 | public class ServiceBusQueueConsumer : IServiceBusQueueConsumer 9 | { 10 | private readonly ILogger _logger; 11 | private readonly string _queueName; 12 | private readonly ServiceBusProcessor _serviceBusProcessor; 13 | private readonly IProcessData _processData; 14 | public ServiceBusQueueConsumer(ILogger logger, 15 | IProcessData processData, 16 | AzureConfiguration azureConfiguration, 17 | ServiceBusClient serviceBusClient) 18 | { 19 | _logger = logger; 20 | _processData = processData; 21 | _queueName = azureConfiguration.ServiceBus.QueueName; 22 | var options = new ServiceBusProcessorOptions 23 | { 24 | // By default or when AutoCompleteMessages is set to true, the processor will complete the message after executing the message handler 25 | // Set AutoCompleteMessages to false to [settle messages](https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock) on your own. 26 | // In both cases, if the message handler throws an exception without settling the message, the processor will abandon the message. 27 | AutoCompleteMessages = false, 28 | 29 | // I can also allow for multi-threading 30 | MaxConcurrentCalls = 2 31 | }; 32 | _serviceBusProcessor = serviceBusClient.CreateProcessor(_queueName, options); 33 | } 34 | 35 | public void RegisterOnMessageHandlerAndReceiveMessages() 36 | { 37 | _logger.LogDebug($"Register queue for {_queueName}"); 38 | _serviceBusProcessor.ProcessMessageAsync += MessageHandler; 39 | _serviceBusProcessor.ProcessErrorAsync += ErrorHandler; 40 | _logger.LogDebug($"Registered queue for {_queueName}"); 41 | } 42 | 43 | private async Task MessageHandler(ProcessMessageEventArgs args) 44 | { 45 | var myPayload = args.Message.Body.ToString(); 46 | _processData.Process(myPayload); 47 | await args.CompleteMessageAsync(args.Message); 48 | } 49 | 50 | private Task ErrorHandler(ProcessErrorEventArgs args) 51 | { 52 | _logger.LogError(args.Exception, "Message handler encountered an exception"); 53 | 54 | _logger.LogDebug($"- Error Source: {args.ErrorSource}"); 55 | _logger.LogDebug($"- Entity Path: {args.EntityPath}"); 56 | _logger.LogDebug($"- Identifier: {args.Identifier}"); 57 | _logger.LogDebug($"- FullyQualifiedNamespace: {args.FullyQualifiedNamespace}"); 58 | 59 | return Task.CompletedTask; 60 | } 61 | 62 | public async Task CloseQueueAsync() 63 | { 64 | await _serviceBusProcessor.CloseAsync(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/ServiceBusTopicSubscription.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Runtime.Internal; 2 | using Azure.Messaging.ServiceBus; 3 | using BervProject.WebApi.Boilerplate.ConfigModel; 4 | using Microsoft.Azure.ServiceBus; 5 | using Microsoft.Extensions.Logging; 6 | using System.Diagnostics; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace BervProject.WebApi.Boilerplate.Services.Azure 12 | { 13 | public class ServiceBusTopicSubscription : IServiceBusTopicSubscription 14 | { 15 | private readonly ILogger _logger; 16 | private readonly string _topicName; 17 | private readonly string _topicSubscription = "topicSubscriptionRandom"; 18 | private readonly ServiceBusProcessor _serviceBusProcessor; 19 | private readonly IProcessData _processData; 20 | public ServiceBusTopicSubscription(ILogger logger, 21 | IProcessData processData, 22 | AzureConfiguration azureConfiguration, 23 | ServiceBusClient serviceBusClient) 24 | { 25 | _logger = logger; 26 | _processData = processData; 27 | _topicName = azureConfiguration.ServiceBus.TopicName; 28 | var options = new ServiceBusProcessorOptions 29 | { 30 | // By default or when AutoCompleteMessages is set to true, the processor will complete the message after executing the message handler 31 | // Set AutoCompleteMessages to false to [settle messages](https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock) on your own. 32 | // In both cases, if the message handler throws an exception without settling the message, the processor will abandon the message. 33 | AutoCompleteMessages = false, 34 | 35 | // I can also allow for multi-threading 36 | MaxConcurrentCalls = 2 37 | }; 38 | _serviceBusProcessor = serviceBusClient.CreateProcessor(_topicName, options); 39 | } 40 | 41 | public async Task CloseSubscriptionClientAsync() 42 | { 43 | await _serviceBusProcessor.CloseAsync(); 44 | } 45 | 46 | public void RegisterOnMessageHandlerAndReceiveMessages() 47 | { 48 | 49 | _logger.LogDebug($"Register topic for {_topicName}/{_topicSubscription}"); 50 | _serviceBusProcessor.ProcessMessageAsync += MessageHandler; 51 | _serviceBusProcessor.ProcessErrorAsync += ErrorHandler; 52 | _logger.LogDebug($"Registered topic for {_topicName}/{_topicSubscription}"); 53 | } 54 | 55 | private async Task MessageHandler(ProcessMessageEventArgs args) 56 | { 57 | var myPayload = args.Message.Body.ToString(); 58 | _processData.Process(myPayload); 59 | await args.CompleteMessageAsync(args.Message); 60 | } 61 | 62 | private Task ErrorHandler(ProcessErrorEventArgs args) 63 | { 64 | _logger.LogError(args.Exception, "Message handler encountered an exception"); 65 | 66 | _logger.LogDebug($"- Error Source: {args.ErrorSource}"); 67 | _logger.LogDebug($"- Entity Path: {args.EntityPath}"); 68 | _logger.LogDebug($"- Identifier: {args.Identifier}"); 69 | _logger.LogDebug($"- FullyQualifiedNamespace: {args.FullyQualifiedNamespace}"); 70 | 71 | return Task.CompletedTask; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/Azure/TopicServices.cs: -------------------------------------------------------------------------------- 1 | using Azure.Messaging.ServiceBus; 2 | using BervProject.WebApi.Boilerplate.ConfigModel; 3 | using Microsoft.Azure.ServiceBus; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BervProject.WebApi.Boilerplate.Services.Azure 10 | { 11 | public class TopicServices : ITopicServices 12 | { 13 | private readonly string _topicName; 14 | private readonly ServiceBusSender _serviceBusSender; 15 | private readonly ILogger _logger; 16 | public TopicServices(AzureConfiguration azureConfiguration, ILogger logger, ServiceBusClient serviceBusClient) 17 | { 18 | _logger = logger; 19 | _topicName = azureConfiguration.ServiceBus.TopicName; 20 | _serviceBusSender = serviceBusClient.CreateSender(_topicName); 21 | } 22 | public async Task SendTopic(string message) 23 | { 24 | try 25 | { 26 | var encodedMessage = new ServiceBusMessage(message); 27 | _logger.LogDebug($"Sending message: {message}"); 28 | await _serviceBusSender.SendMessageAsync(encodedMessage); 29 | _logger.LogDebug($"Sent message: {message}"); 30 | return true; 31 | } 32 | catch (Exception ex) 33 | { 34 | _logger.LogError(ex.ToString()); 35 | return false; 36 | } 37 | finally 38 | { 39 | await _serviceBusSender.CloseAsync(); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/CronService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Services 4 | { 5 | public class CronService : ICronService 6 | { 7 | private readonly ILogger _logger; 8 | public CronService(ILoggerFactory loggerFactory) 9 | { 10 | _logger = loggerFactory.CreateLogger(); 11 | } 12 | public void HelloWorld() 13 | { 14 | _logger.LogDebug("Run Cron"); 15 | _logger.LogInformation("Hello World Cron!"); 16 | _logger.LogDebug("Finished Run Cron"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/ICronService.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.Services 2 | { 3 | public interface ICronService 4 | { 5 | public void HelloWorld(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/IProcessData.cs: -------------------------------------------------------------------------------- 1 | namespace BervProject.WebApi.Boilerplate.Services 2 | { 3 | public interface IProcessData 4 | { 5 | void Process(string message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/Services/ProcessData.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace BervProject.WebApi.Boilerplate.Services 4 | { 5 | public class ProcessData : IProcessData 6 | { 7 | private readonly ILogger _logger; 8 | public ProcessData(ILogger logger) 9 | { 10 | _logger = logger; 11 | } 12 | public void Process(string message) 13 | { 14 | _logger.LogDebug($"You get message: {message}"); 15 | // TODO: another handler 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Trace", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BervProject.WebApi.Boilerplate/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Trace", 5 | "Microsoft": "Information", 6 | "Microsoft.Hosting.Lifetime": "Information", 7 | "Hangfire": "Information" 8 | } 9 | }, 10 | "AllowedHosts": "*", 11 | "AWS": { 12 | "Basic": { 13 | "Auth": { 14 | "AccessKey": "", 15 | "SecretKey": "" 16 | } 17 | }, 18 | "Email": { 19 | "Location": "us-east-1" 20 | }, 21 | "Dynamo": { 22 | "Location": "ap-southeast-1" 23 | } 24 | }, 25 | "Azure": { 26 | "Storage": { 27 | "Blob": { 28 | "ContainerName": "testblob" 29 | }, 30 | "Queue": { 31 | "QueueName": "testqueue" 32 | } 33 | }, 34 | "ServiceBus": { 35 | "QueueName": "testqueue", 36 | "TopicName": "testtopic" 37 | } 38 | }, 39 | "ConnectionStrings": { 40 | "BoilerplateConnectionString": "Host=localhost;Database=testdb;Username=postgres;Password=devpass4444", 41 | "AzureStorageBlob": "UseDevelopmentStorage=true", 42 | "AzureStorageQueue": "UseDevelopmentStorage=true", 43 | "AzureStorageTable": "UseDevelopmentStorage=true", 44 | "AzureServiceBus": "", 45 | "Redis": "localhost:6379" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/BervProject.WebApi.Integration.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | false 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | Always 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/BlobControllerTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | using BervProject.WebApi.Integration.Test.Fixtures; 3 | using Microsoft.AspNetCore.Mvc.Testing; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace BervProject.WebApi.Integration.Test 7 | { 8 | [Collection("Webapp")] 9 | public class BlobControllerTest 10 | { 11 | private readonly WebApplicationFactory _applicationFactory; 12 | public BlobControllerTest(WebAppFixture webAppFixtures) 13 | { 14 | this._applicationFactory = webAppFixtures.WebApp; 15 | } 16 | [Fact] 17 | public async Task UploadBlobTest() 18 | { 19 | var client = _applicationFactory.CreateClient(); 20 | var response = await client.PostAsync("/api/v1.0/blob/create", null); 21 | Assert.True(response.IsSuccessStatusCode); 22 | using var file1 = File.OpenRead(@"Docs/test.txt"); 23 | using var content1 = new StreamContent(file1); 24 | using var formData = new MultipartFormDataContent(); 25 | formData.Add(content1, "file", "test.txt"); 26 | response = await client.PostAsync("/api/v1.0/blob/upload", formData); 27 | Assert.True(response.IsSuccessStatusCode); 28 | response = await client.GetAsync("/api/v1.0/blob/list"); 29 | Assert.True(response.IsSuccessStatusCode); 30 | var data = await response.Content.ReadFromJsonAsync>>(); 31 | Assert.NotNull(data); 32 | Assert.Single(data); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/Collections/WebAppCollection.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Integration.Test.Fixtures; 2 | 3 | namespace BervProject.WebApi.Integration.Test.Collections 4 | { 5 | [CollectionDefinition("Webapp")] 6 | public class WebAppCollection : ICollectionFixture 7 | { 8 | // This class has no code, and is never created. Its purpose is simply 9 | // to be the place to apply [CollectionDefinition] and all the 10 | // ICollectionFixture<> interfaces. 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/CronControllerTest.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Integration.Test.Fixtures; 2 | using Hangfire; 3 | using Microsoft.AspNetCore.Mvc.Testing; 4 | 5 | namespace BervProject.WebApi.Integration.Test 6 | { 7 | [Collection("Webapp")] 8 | public class CronControllerTest : IDisposable 9 | { 10 | private readonly WebApplicationFactory _applicationFactory; 11 | private readonly List _registeredRecurring = new List(); 12 | public CronControllerTest(WebAppFixture webAppFixtures) 13 | { 14 | this._applicationFactory = webAppFixtures.WebApp; 15 | } 16 | 17 | public void Dispose() 18 | { 19 | RemoveRecurringJob(); 20 | } 21 | 22 | private void RemoveRecurringJob() 23 | { 24 | var cronClient = (IRecurringJobManager?)this._applicationFactory.Services.GetService(typeof(IRecurringJobManager)); 25 | if (cronClient != null) 26 | { 27 | foreach (var cronId in _registeredRecurring) 28 | { 29 | cronClient.RemoveIfExists(cronId); 30 | } 31 | } 32 | } 33 | 34 | [Fact] 35 | public async Task SuccessCreateCronOnceTest() 36 | { 37 | var client = _applicationFactory.CreateClient(); 38 | var response = await client.PostAsync("/api/v1.0/cron/CreateCronOnce", null); 39 | Assert.True(response.IsSuccessStatusCode); 40 | var stringResponse = await response.Content.ReadAsStringAsync(); 41 | Assert.NotEmpty(stringResponse); 42 | var cronClient = (IBackgroundJobClient?)this._applicationFactory.Services.GetService(typeof(IBackgroundJobClient)); 43 | if (cronClient != null) 44 | { 45 | var deleted = cronClient.Delete(stringResponse); 46 | Assert.True(deleted); 47 | } 48 | } 49 | 50 | [Fact] 51 | public async Task SuccessCreateRecuranceTest() 52 | { 53 | var client = _applicationFactory.CreateClient(); 54 | var response = await client.PostAsync("/api/v1.0/cron/CreateRecurance", null); 55 | Assert.True(response.IsSuccessStatusCode); 56 | var stringResponse = await response.Content.ReadAsStringAsync(); 57 | Assert.NotEmpty(stringResponse); 58 | _registeredRecurring.Add(stringResponse); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/Docs/test.txt: -------------------------------------------------------------------------------- 1 | My test file -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/ErrorControllerTest.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Integration.Test.Fixtures; 2 | using Microsoft.AspNetCore.Mvc.Testing; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace BervProject.WebApi.Integration.Test 6 | { 7 | [Collection("Webapp")] 8 | public class ErrorControllerTest 9 | { 10 | private readonly WebApplicationFactory _applicationFactory; 11 | public ErrorControllerTest(WebAppFixture webAppFixtures) 12 | { 13 | this._applicationFactory = webAppFixtures.WebApp; 14 | } 15 | [Fact] 16 | public async Task SuccessCheck() 17 | { 18 | var client = _applicationFactory.CreateClient(); 19 | var response = await client.GetAsync("/api/v1.0/error/error"); 20 | Assert.False(response.IsSuccessStatusCode); 21 | Assert.Equal(System.Net.HttpStatusCode.InternalServerError, response.StatusCode); 22 | var responseString = await response.Content.ReadAsStringAsync(); 23 | var jObject = JObject.Parse(responseString); 24 | var status = jObject.Value("status"); 25 | var title = jObject.Value("title"); 26 | Assert.Equal("An error occurred while processing your request.", title); 27 | Assert.Equal(500, status); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/Fixtures/WebAppFixture.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Testing; 2 | 3 | namespace BervProject.WebApi.Integration.Test.Fixtures 4 | { 5 | public class WebAppFixture : IDisposable 6 | { 7 | public WebAppFixture() 8 | { 9 | WebApp = new WebApplicationFactory() 10 | .WithWebHostBuilder(builder => 11 | { 12 | // ... Configure test services 13 | }); 14 | 15 | } 16 | 17 | public void Dispose() => WebApp.Dispose(); 18 | 19 | public WebApplicationFactory WebApp { get; private set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/HealthCheckTest.cs: -------------------------------------------------------------------------------- 1 | using BervProject.WebApi.Integration.Test.Fixtures; 2 | using Microsoft.AspNetCore.Mvc.Testing; 3 | 4 | namespace BervProject.WebApi.Integration.Test 5 | { 6 | [Collection("Webapp")] 7 | public class HealthCheckTest 8 | { 9 | private readonly WebApplicationFactory _applicationFactory; 10 | public HealthCheckTest(WebAppFixture webAppFixtures) 11 | { 12 | this._applicationFactory = webAppFixtures.WebApp; 13 | } 14 | [Fact] 15 | public async Task SuccessCheck() 16 | { 17 | var client = _applicationFactory.CreateClient(); 18 | var response = await client.GetAsync("/health"); 19 | Assert.True(response.IsSuccessStatusCode); 20 | var stringResponse = await response.Content.ReadAsStringAsync(); 21 | Assert.Equal("Healthy", stringResponse); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/NoteControllerTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | using BervProject.WebApi.Integration.Test.Fixtures; 3 | using BervProject.WebApi.Boilerplate.Entities; 4 | using Microsoft.AspNetCore.Mvc.Testing; 5 | 6 | namespace BervProject.WebApi.Integration.Test 7 | { 8 | [Collection("Webapp")] 9 | public class NoteControllerTest 10 | { 11 | private readonly WebApplicationFactory _applicationFactory; 12 | public NoteControllerTest(WebAppFixture webAppFixtures) 13 | { 14 | this._applicationFactory = webAppFixtures.WebApp; 15 | } 16 | [Fact] 17 | public async Task CreateNoteTest() 18 | { 19 | var client = _applicationFactory.CreateClient(); 20 | var response = await client.PostAsync("/api/v1.0/note/createTable", null); 21 | Assert.True(response.IsSuccessStatusCode); 22 | var stringResponse = await response.Content.ReadAsStringAsync(); 23 | Assert.Equal("true", stringResponse); 24 | var partitionKey = "part-1"; 25 | var rowKey = "row-1"; 26 | var title = "Hello World!"; 27 | var message = "Yes!"; 28 | var newNote = new Note() 29 | { 30 | PartitionKey = partitionKey, 31 | RowKey = rowKey, 32 | Title = title, 33 | Message = message, 34 | }; 35 | response = await client.PostAsJsonAsync("/api/v1.0/note/upsert", newNote); 36 | Assert.True(response.IsSuccessStatusCode); 37 | stringResponse = await response.Content.ReadAsStringAsync(); 38 | Assert.Equal("true", stringResponse); 39 | response = await client.GetAsync($"/api/v1.0/note/get?partitionKey={partitionKey}&rowKey={rowKey}"); 40 | Assert.True(response.IsSuccessStatusCode); 41 | var data = await response.Content.ReadFromJsonAsync(); 42 | Assert.NotNull(data); 43 | Assert.Equal(title, data.Title); 44 | Assert.Equal(message, data.Message); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/StorageQueueControllerTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | using System.Text; 3 | using System.Text.Json; 4 | using BervProject.WebApi.Boilerplate.Models; 5 | using BervProject.WebApi.Boilerplate.Models.Response; 6 | using BervProject.WebApi.Integration.Test.Fixtures; 7 | using Microsoft.AspNetCore.Mvc.Testing; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace BervProject.WebApi.Integration.Test 11 | { 12 | [Collection("Webapp")] 13 | public class StorageQueueControllerTest 14 | { 15 | private readonly WebApplicationFactory _applicationFactory; 16 | public StorageQueueControllerTest(WebAppFixture webAppFixtures) 17 | { 18 | this._applicationFactory = webAppFixtures.WebApp; 19 | } 20 | [Fact] 21 | public async Task StorageQueueSendMessageTest() 22 | { 23 | var client = _applicationFactory.CreateClient(); 24 | var messageData = new MessageData{ 25 | Message = "Hello World!" 26 | }; 27 | using var content = new StringContent(JsonSerializer.Serialize(messageData), Encoding.UTF8, "application/json"); 28 | var response = await client.PostAsync("/api/v1.0/storagequeue/sendMessage", content); 29 | Assert.True(response.IsSuccessStatusCode); 30 | var data = await response.Content.ReadFromJsonAsync(); 31 | Assert.NotNull(data); 32 | Assert.True(data.IsSuccess); 33 | response = await client.GetAsync("/api/v1.0/storagequeue/receiveMessage"); 34 | Assert.True(response.IsSuccessStatusCode); 35 | data = await response.Content.ReadFromJsonAsync(); 36 | Assert.NotNull(data); 37 | Assert.True(data.IsSuccess); 38 | Assert.Equal("Hello World!", data.YourMessage); 39 | 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /BervProject.WebApi.Integration.Test/WeatherForecastControllerTest.cs: -------------------------------------------------------------------------------- 1 |  2 | using BervProject.WebApi.Boilerplate.Entities; 3 | using BervProject.WebApi.Integration.Test.Fixtures; 4 | using Microsoft.AspNetCore.Mvc.Testing; 5 | using System.Net.Http.Json; 6 | 7 | namespace BervProject.WebApi.Integration.Test 8 | { 9 | [Collection("Webapp")] 10 | public class WeatherForecastControllerTest 11 | { 12 | private readonly WebApplicationFactory _applicationFactory; 13 | public WeatherForecastControllerTest(WebAppFixture webAppFixtures) 14 | { 15 | this._applicationFactory = webAppFixtures.WebApp; 16 | } 17 | [Fact] 18 | public async Task SuccessCheck() 19 | { 20 | var client = _applicationFactory.CreateClient(); 21 | var response = await client.GetAsync("/api/v1.0/weatherforecast/db"); 22 | Assert.True(response.IsSuccessStatusCode); 23 | var books = await response.Content.ReadFromJsonAsync>(); 24 | Assert.NotNull(books); 25 | Assert.Equal(2, books.Count); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /BervProject.WebApi.Test/BervProject.WebApi.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | false 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | all 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/AWS/AWSS3ServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Amazon.S3; 2 | using Amazon.S3.Model; 3 | using Autofac.Extras.Moq; 4 | using BervProject.WebApi.Boilerplate.Services.AWS; 5 | using Microsoft.AspNetCore.Http; 6 | using Moq; 7 | using System.IO; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | namespace BervProject.WebApi.Test.Services.AWS 12 | { 13 | public class AWSS3ServiceTest 14 | { 15 | 16 | [Fact] 17 | public async Task UploadFileSuccess() 18 | { 19 | using var mock = AutoMock.GetLoose(); 20 | var amazonS3Mock = mock.Mock(); 21 | var fileMock = mock.Mock(); 22 | var nullStream = new MemoryStream(0); 23 | var fileName = "ok.jpg"; 24 | var contentType = "image/jpeg"; 25 | amazonS3Mock.Setup(x => x.PutObjectAsync(It.IsAny(), default)); 26 | fileMock.SetupGet(x => x.FileName).Returns(fileName); 27 | fileMock.SetupGet(x => x.ContentType).Returns(contentType); 28 | fileMock.Setup(x => x.OpenReadStream()).Returns(nullStream); 29 | var awsS3Service = mock.Create(); 30 | var result = await awsS3Service.UploadFile(fileMock.Object); 31 | Assert.Equal($"uploads/{fileName}", result); 32 | fileMock.Verify(x => x.OpenReadStream(), Times.Once()); 33 | amazonS3Mock.Verify(x => x.PutObjectAsync(It.IsAny(), default), Times.Once()); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/AWS/DynamoDbServicesTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Amazon.DynamoDBv2; 4 | using Amazon.DynamoDBv2.Model; 5 | using Autofac.Extras.Moq; 6 | using BervProject.WebApi.Boilerplate.Services.AWS; 7 | using Microsoft.Extensions.Logging; 8 | using Moq; 9 | using Xunit; 10 | 11 | namespace BervProject.WebApi.Test.Services.AWS 12 | { 13 | public class DynamoDbServicesTest 14 | { 15 | [Fact] 16 | public async Task Test_CreateObject() 17 | { 18 | using var mock = AutoMock.GetLoose(); 19 | var amazonDynamoDBMock = mock.Mock(); 20 | var logMock = mock.Mock>(); 21 | amazonDynamoDBMock.Setup(x => x.PutItemAsync(It.IsAny(), default)) 22 | .Returns(Task.FromResult(new PutItemResponse 23 | { 24 | HttpStatusCode = System.Net.HttpStatusCode.OK, 25 | Attributes = new System.Collections.Generic.Dictionary 26 | { 27 | {"mock", new AttributeValue ("Hello")} 28 | } 29 | })); 30 | var dynamoDbServices = mock.Create(); 31 | await dynamoDbServices.CreateObject(); 32 | amazonDynamoDBMock.Verify(x => x.PutItemAsync(It.IsAny(), default), Times.Once()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/AWS/EmailServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Amazon.SimpleEmail; 2 | using Amazon.SimpleEmail.Model; 3 | using Autofac.Extras.Moq; 4 | using BervProject.WebApi.Boilerplate.Services.AWS; 5 | using Microsoft.Extensions.Logging; 6 | using Moq; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | using Xunit; 11 | 12 | namespace BervProject.WebApi.Test.Services.AWS 13 | { 14 | public class EmailServiceTest 15 | { 16 | [Fact] 17 | public async Task SendEmailSuccess() 18 | { 19 | using var mock = AutoMock.GetLoose(); 20 | var mockEmailService = mock.Mock(); 21 | var logMock = mock.Mock>(); 22 | var reciever = new List 23 | { 24 | "myreceiver@receiver.com" 25 | }; 26 | var sendEmailResponse = new SendEmailResponse() 27 | { 28 | HttpStatusCode = System.Net.HttpStatusCode.OK, 29 | MessageId = "random", 30 | ResponseMetadata = new Amazon.Runtime.ResponseMetadata() 31 | }; 32 | mockEmailService.Setup(x => x.SendEmailAsync(It.IsAny(), default)) 33 | .Returns(Task.FromResult(sendEmailResponse)); 34 | var emailService = mock.Create(); 35 | await emailService.SendEmail(reciever); 36 | mockEmailService.Verify(x => x.SendEmailAsync(It.IsAny(), default), Times.Once()); 37 | } 38 | 39 | [Fact] 40 | public async Task SendEmailFailed() 41 | { 42 | using var mock = AutoMock.GetLoose(); 43 | var mockEmailService = mock.Mock(); 44 | var logMock = mock.Mock>(); 45 | var reciever = new List 46 | { 47 | "myreceiver@receiver.com" 48 | }; 49 | var sendEmailResponse = new SendEmailResponse() 50 | { 51 | HttpStatusCode = System.Net.HttpStatusCode.InternalServerError, 52 | MessageId = "random", 53 | ResponseMetadata = new Amazon.Runtime.ResponseMetadata() 54 | }; 55 | mockEmailService.Setup(x => x.SendEmailAsync(It.IsAny(), default)) 56 | .Returns(Task.FromResult(sendEmailResponse)); 57 | var emailService = mock.Create(); 58 | await emailService.SendEmail(reciever); 59 | mockEmailService.Verify(x => x.SendEmailAsync(It.IsAny(), default), Times.Once()); 60 | logMock.Verify(l => 61 | l.Log( 62 | LogLevel.Warning, 63 | It.IsAny(), 64 | It.Is((state, type) => state.ToString().Equals("There is a problem when sending email")), 65 | null, 66 | (Func)It.IsAny() 67 | ), 68 | Times.Once() 69 | ); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/Azure/AzureQueueServicesTest.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Autofac.Extras.Moq; 3 | using Azure.Messaging.ServiceBus; 4 | using BervProject.WebApi.Boilerplate.ConfigModel; 5 | using BervProject.WebApi.Boilerplate.Services.Azure; 6 | using Microsoft.Extensions.Logging; 7 | using Moq; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | namespace BervProject.WebApi.Test.Services.Azure 12 | { 13 | public class AzureQueueServiceTest 14 | { 15 | [Fact] 16 | public async Task SendHelloToQueueServiceBus() 17 | { 18 | var azureConfig = new AzureConfiguration 19 | { 20 | ServiceBus = new AzureServiceBus 21 | { 22 | QueueName = "" 23 | } 24 | }; 25 | var message = "Hello!"; 26 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 27 | var loggerMock = mock.Mock>(); 28 | var serviceBusClient = mock.Mock(); 29 | var serviceBusSender = mock.Mock(); 30 | serviceBusClient.Setup(x => x.CreateSender("")).Returns(serviceBusSender.Object); 31 | serviceBusSender.Setup(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default)).Returns(Task.CompletedTask); 32 | var queueService = mock.Create(); 33 | var ok = await queueService.SendMessage("Hello!"); 34 | serviceBusSender.Verify(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default), Times.Once()); 35 | serviceBusSender.Verify(x => x.CloseAsync(default), Times.Once); 36 | Assert.True(ok); 37 | } 38 | 39 | [Fact] 40 | public async Task SendMessageFailedTest() 41 | { 42 | var azureConfig = new AzureConfiguration 43 | { 44 | ServiceBus = new AzureServiceBus 45 | { 46 | QueueName = "" 47 | } 48 | }; 49 | var message = "Hello!"; 50 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 51 | var loggerMock = mock.Mock>(); 52 | var serviceBusClient = mock.Mock(); 53 | var serviceBusSender = mock.Mock(); 54 | serviceBusClient.Setup(x => x.CreateSender("")).Returns(serviceBusSender.Object); 55 | serviceBusSender.Setup(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default)).Throws(); 56 | var queueService = mock.Create(); 57 | var ok = await queueService.SendMessage("Hello!"); 58 | serviceBusSender.Verify(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default), Times.Once()); 59 | serviceBusSender.Verify(x => x.CloseAsync(default), Times.Once); 60 | Assert.False(ok); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/Azure/AzureStorageQueueServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Autofac.Extras.Moq; 3 | using Azure.Storage.Queues; 4 | using Azure.Storage.Queues.Models; 5 | using BervProject.WebApi.Boilerplate.ConfigModel; 6 | using BervProject.WebApi.Boilerplate.Services.Azure; 7 | using Microsoft.Extensions.Logging; 8 | using Moq; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | using Xunit; 15 | 16 | namespace BervProject.WebApi.Test.Services.Azure 17 | { 18 | public class AzureStorageQueueServiceTest 19 | { 20 | [Fact] 21 | public void ReceiveEmptyMessageTest() 22 | { 23 | var azureConfig = new AzureConfiguration 24 | { 25 | Storage = new AzureStorage 26 | { 27 | Queue = new StorageQueue 28 | { 29 | QueueName = "" 30 | } 31 | } 32 | }; 33 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 34 | var loggerMock = mock.Mock>(); 35 | var queueServiceClient = mock.Mock(); 36 | var mockQueue = mock.Mock(); 37 | queueServiceClient.Setup(x => x.GetQueueClient("")).Returns(mockQueue.Object); 38 | mockQueue.Setup(x => x.CreateIfNotExists(null, default)); 39 | mockQueue.Setup(x => x.Exists(default).Value).Returns(true); 40 | mockQueue.Setup(x => x.ReceiveMessage(It.IsAny(), default).Value).Returns(() => null); 41 | var queueService = mock.Create(); 42 | var emptyResult = queueService.ReceiveMessage(); 43 | mockQueue.Verify(x => x.Exists(default), Times.Once()); 44 | mockQueue.Verify(x => x.ReceiveMessage(It.IsAny(), default), Times.Once()); 45 | Assert.Null(emptyResult); 46 | } 47 | 48 | [Fact] 49 | public void ReceiveEmptyMessageBecauseQueueNotFoundTest() 50 | { 51 | var azureConfig = new AzureConfiguration 52 | { 53 | Storage = new AzureStorage 54 | { 55 | Queue = new StorageQueue 56 | { 57 | QueueName = "" 58 | } 59 | } 60 | }; 61 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 62 | var loggerMock = mock.Mock>(); 63 | var queueServiceClient = mock.Mock(); 64 | var mockQueue = mock.Mock(); 65 | queueServiceClient.Setup(x => x.GetQueueClient("")).Returns(mockQueue.Object); 66 | mockQueue.Setup(x => x.CreateIfNotExists(null, default)); 67 | mockQueue.Setup(x => x.Exists(default).Value).Returns(false); 68 | mockQueue.Setup(x => x.ReceiveMessage(It.IsAny(), default).Value).Returns(() => null); 69 | var queueService = mock.Create(); 70 | var emptyResult = queueService.ReceiveMessage(); 71 | mockQueue.Verify(x => x.Exists(default), Times.Once()); 72 | mockQueue.Verify(x => x.ReceiveMessage(It.IsAny(), default), Times.Never()); 73 | Assert.Null(emptyResult); 74 | } 75 | 76 | [Fact] 77 | public void ReceiveEmptyMessageBecauseExceptionTest() 78 | { 79 | var azureConfig = new AzureConfiguration 80 | { 81 | Storage = new AzureStorage 82 | { 83 | Queue = new StorageQueue 84 | { 85 | QueueName = "" 86 | } 87 | } 88 | }; 89 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 90 | var loggerMock = mock.Mock>(); 91 | var queueServiceClient = mock.Mock(); 92 | var mockQueue = mock.Mock(); 93 | queueServiceClient.Setup(x => x.GetQueueClient("")).Returns(mockQueue.Object); 94 | mockQueue.Setup(x => x.CreateIfNotExists(null, default)); 95 | mockQueue.Setup(x => x.Exists(default).Value).Returns(false); 96 | mockQueue.Setup(x => x.ReceiveMessage(It.IsAny(), default).Value).Throws(); 97 | var queueService = mock.Create(); 98 | var emptyResult = queueService.ReceiveMessage(); 99 | mockQueue.Verify(x => x.Exists(default), Times.Once()); 100 | mockQueue.Verify(x => x.ReceiveMessage(It.IsAny(), default), Times.Never()); 101 | Assert.Null(emptyResult); 102 | } 103 | 104 | [Fact] 105 | public void SendMessageSuccessTest() 106 | { 107 | var azureConfig = new AzureConfiguration 108 | { 109 | Storage = new AzureStorage 110 | { 111 | Queue = new StorageQueue 112 | { 113 | QueueName = "" 114 | } 115 | } 116 | }; 117 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 118 | var loggerMock = mock.Mock>(); 119 | var queueServiceClient = mock.Mock(); 120 | var mockQueue = mock.Mock(); 121 | queueServiceClient.Setup(x => x.GetQueueClient("")).Returns(mockQueue.Object); 122 | mockQueue.Setup(x => x.CreateIfNotExists(null, default)); 123 | mockQueue.Setup(x => x.Exists(default).Value).Returns(true); 124 | var message = "boba"; 125 | mockQueue.Setup(x => x.SendMessage(It.IsAny(), default)).Returns(() => null); 126 | var queueService = mock.Create(); 127 | var success = queueService.SendMessage(message); 128 | mockQueue.Verify(x => x.Exists(default), Times.Once()); 129 | Assert.True(success); 130 | } 131 | 132 | [Fact] 133 | public void SendMessageFailedTest() 134 | { 135 | var azureConfig = new AzureConfiguration 136 | { 137 | Storage = new AzureStorage 138 | { 139 | Queue = new StorageQueue 140 | { 141 | QueueName = "" 142 | } 143 | } 144 | }; 145 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 146 | var loggerMock = mock.Mock>(); 147 | var queueServiceClient = mock.Mock(); 148 | var mockQueue = mock.Mock(); 149 | queueServiceClient.Setup(x => x.GetQueueClient("")).Returns(mockQueue.Object); 150 | mockQueue.Setup(x => x.CreateIfNotExists(null, default)); 151 | mockQueue.Setup(x => x.Exists(default).Value).Returns(false); 152 | var message = "boba"; 153 | mockQueue.Setup(x => x.SendMessage(It.IsAny(), default)).Returns(() => null); 154 | var queueService = mock.Create(); 155 | var success = queueService.SendMessage(message); 156 | mockQueue.Verify(x => x.Exists(default), Times.Once()); 157 | Assert.False(success); 158 | } 159 | 160 | [Fact] 161 | public void SendMessageFailedBecauseExceptionTest() 162 | { 163 | var azureConfig = new AzureConfiguration 164 | { 165 | Storage = new AzureStorage 166 | { 167 | Queue = new StorageQueue 168 | { 169 | QueueName = "" 170 | } 171 | } 172 | }; 173 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 174 | var loggerMock = mock.Mock>(); 175 | var queueServiceClient = mock.Mock(); 176 | var mockQueue = mock.Mock(); 177 | queueServiceClient.Setup(x => x.GetQueueClient("")).Returns(mockQueue.Object); 178 | mockQueue.Setup(x => x.CreateIfNotExists(null, default)); 179 | mockQueue.Setup(x => x.Exists(default).Value).Returns(false); 180 | var message = "boba"; 181 | mockQueue.Setup(x => x.SendMessage(It.IsAny(), default)).Throws(); 182 | var queueService = mock.Create(); 183 | var success = queueService.SendMessage(message); 184 | mockQueue.Verify(x => x.Exists(default), Times.Once()); 185 | Assert.False(success); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/Azure/BlobServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Autofac.Extras.Moq; 3 | using Azure.Storage.Blobs; 4 | using Azure.Storage.Blobs.Models; 5 | using BervProject.WebApi.Boilerplate.ConfigModel; 6 | using BervProject.WebApi.Boilerplate.Services.Azure; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.Logging; 9 | using Moq; 10 | using System.IO; 11 | using Xunit; 12 | 13 | namespace BervProject.WebApi.Test.Services.Azure 14 | { 15 | public class BlobServiceTest 16 | { 17 | [Fact] 18 | public void CreateStorageTest() 19 | { 20 | var azureConfig = new AzureConfiguration 21 | { 22 | Storage = new AzureStorage 23 | { 24 | Blob = new BlobStorage 25 | { 26 | ContainerName = "" 27 | } 28 | } 29 | }; 30 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 31 | var loggerMock = mock.Mock>(); 32 | var blobServiceClient = mock.Mock(); 33 | var mockContainer = mock.Mock(); 34 | blobServiceClient.Setup(x => x.GetBlobContainerClient("")).Returns(mockContainer.Object); 35 | var blobService = mock.Create(); 36 | blobService.CreateStorageContainer(); 37 | mockContainer.Verify(x => x.CreateIfNotExists(PublicAccessType.None, null, null, default), Times.Once()); 38 | } 39 | 40 | [Fact] 41 | public void GettingEmptyBlobWhenContainerNotExists() 42 | { 43 | var azureConfig = new AzureConfiguration 44 | { 45 | Storage = new AzureStorage 46 | { 47 | Blob = new BlobStorage 48 | { 49 | ContainerName = "" 50 | } 51 | } 52 | }; 53 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 54 | var loggerMock = mock.Mock>(); 55 | var blobServiceClient = mock.Mock(); 56 | var mockContainer = mock.Mock(); 57 | blobServiceClient.Setup(x => x.GetBlobContainerClient("")).Returns(mockContainer.Object); 58 | mockContainer.Setup(x => x.Exists(default).Value).Returns(false); 59 | var blobService = mock.Create(); 60 | var listResult = blobService.GetBlobsInfo(); 61 | mockContainer.Verify(x => x.Exists(default), Times.Once()); 62 | Assert.Empty(listResult); 63 | } 64 | 65 | [Fact] 66 | public void UploadFileWhenContainerExist() 67 | { 68 | var azureConfig = new AzureConfiguration 69 | { 70 | Storage = new AzureStorage 71 | { 72 | Blob = new BlobStorage 73 | { 74 | ContainerName = "" 75 | } 76 | } 77 | }; 78 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 79 | var loggerMock = mock.Mock>(); 80 | var blobServiceClient = mock.Mock(); 81 | var mockContainer = mock.Mock(); 82 | var mockBlobClient = mock.Mock(); 83 | var fileMock = mock.Mock(); 84 | var nullStream = new MemoryStream(0); 85 | blobServiceClient.Setup(x => x.GetBlobContainerClient("")).Returns(mockContainer.Object); 86 | mockContainer.Setup(x => x.Exists(default).Value).Returns(true); 87 | var fileName = "Dummy"; 88 | mockContainer.Setup(x => x.GetBlobClient(fileName)).Returns(mockBlobClient.Object); 89 | fileMock.SetupGet(x => x.FileName).Returns(fileName); 90 | fileMock.Setup(x => x.OpenReadStream()).Returns(nullStream); 91 | var blobService = mock.Create(); 92 | blobService.UploadFile(fileMock.Object); 93 | fileMock.Verify(x => x.OpenReadStream(), Times.Once()); 94 | mockContainer.Verify(x => x.GetBlobClient(fileName), Times.Once()); 95 | mockBlobClient.Verify(x => x.Upload(It.IsAny(), true, default), Times.Once()); 96 | } 97 | 98 | [Fact] 99 | public void UploadFileWhenContainerNotExist() 100 | { 101 | var azureConfig = new AzureConfiguration 102 | { 103 | Storage = new AzureStorage 104 | { 105 | Blob = new BlobStorage 106 | { 107 | ContainerName = "" 108 | } 109 | } 110 | }; 111 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 112 | var loggerMock = mock.Mock>(); 113 | var blobServiceClient = mock.Mock(); 114 | var mockContainer = mock.Mock(); 115 | var fileMock = mock.Mock(); 116 | var nullStream = new MemoryStream(0); 117 | blobServiceClient.Setup(x => x.GetBlobContainerClient("")).Returns(mockContainer.Object); 118 | mockContainer.Setup(x => x.Exists(default).Value).Returns(false); 119 | var fileName = "Dummy"; 120 | fileMock.SetupGet(x => x.FileName).Returns(fileName); 121 | fileMock.Setup(x => x.OpenReadStream()).Returns(nullStream); 122 | var blobService = mock.Create(); 123 | blobService.UploadFile(fileMock.Object); 124 | fileMock.Verify(x => x.OpenReadStream(), Times.Never()); 125 | mockContainer.Verify(x => x.UploadBlob(fileName, nullStream, default), Times.Never()); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/Azure/TopicServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Autofac.Extras.Moq; 3 | using Azure.Messaging.ServiceBus; 4 | using Azure.Storage.Queues; 5 | using Azure.Storage.Queues.Models; 6 | using BervProject.WebApi.Boilerplate.ConfigModel; 7 | using BervProject.WebApi.Boilerplate.Services.Azure; 8 | using Microsoft.Extensions.Logging; 9 | using Moq; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | using Xunit; 16 | 17 | namespace BervProject.WebApi.Test.Services.Azure 18 | { 19 | public class TopicServiceTest 20 | { 21 | [Fact] 22 | public async Task SendHelloToTopicServiceBus() 23 | { 24 | var azureConfig = new AzureConfiguration 25 | { 26 | ServiceBus = new AzureServiceBus 27 | { 28 | TopicName = "" 29 | } 30 | }; 31 | var message = "Hello!"; 32 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 33 | var loggerMock = mock.Mock>(); 34 | var serviceBusClient = mock.Mock(); 35 | var serviceBusSender = mock.Mock(); 36 | serviceBusClient.Setup(x => x.CreateSender("")).Returns(serviceBusSender.Object); 37 | serviceBusSender.Setup(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default)).Returns(Task.CompletedTask); 38 | var queueService = mock.Create(); 39 | var ok = await queueService.SendTopic("Hello!"); 40 | serviceBusSender.Verify(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default), Times.Once()); 41 | serviceBusSender.Verify(x => x.CloseAsync(default), Times.Once); 42 | Assert.True(ok); 43 | } 44 | 45 | [Fact] 46 | public async Task SendMessageFailedTest() 47 | { 48 | var azureConfig = new AzureConfiguration 49 | { 50 | ServiceBus = new AzureServiceBus 51 | { 52 | TopicName = "" 53 | } 54 | }; 55 | var message = "Hello!"; 56 | using var mock = AutoMock.GetLoose(cfg => cfg.RegisterInstance(azureConfig).As()); 57 | var loggerMock = mock.Mock>(); 58 | var serviceBusClient = mock.Mock(); 59 | var serviceBusSender = mock.Mock(); 60 | serviceBusClient.Setup(x => x.CreateSender("")).Returns(serviceBusSender.Object); 61 | serviceBusSender.Setup(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default)).Throws(); 62 | var queueService = mock.Create(); 63 | var ok = await queueService.SendTopic("Hello!"); 64 | serviceBusSender.Verify(x => x.SendMessageAsync(It.Is(x => x.Body.ToString() == message), default), Times.Once()); 65 | serviceBusSender.Verify(x => x.CloseAsync(default), Times.Once); 66 | Assert.False(ok); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/CronServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Autofac.Extras.Moq; 2 | using BervProject.WebApi.Boilerplate.Services; 3 | using Microsoft.Extensions.Logging; 4 | using Moq; 5 | using System; 6 | using Xunit; 7 | 8 | namespace BervProject.WebApi.Test.Services 9 | { 10 | public class CronServiceTest 11 | { 12 | 13 | [Fact] 14 | public void ProcessDataSuccess() 15 | { 16 | using var mock = AutoMock.GetLoose(); 17 | var loggerMock = mock.Mock>(); 18 | var loggerFactoryMock = mock.Mock(); 19 | loggerFactoryMock.Setup(x => x.CreateLogger(It.IsAny())).Returns(loggerMock.Object); 20 | var processData = mock.Create(); 21 | processData.HelloWorld(); 22 | loggerMock.Verify(logger => logger.Log( 23 | It.Is(logLevel => logLevel == LogLevel.Debug), 24 | It.Is(eventId => eventId.Id == 0), 25 | It.Is((@object, @type) => @object.ToString() == "Run Cron" && @type.Name == "FormattedLogValues"), 26 | It.IsAny(), 27 | It.IsAny>()), 28 | Times.Once); 29 | loggerMock.Verify(logger => logger.Log( 30 | It.Is(logLevel => logLevel == LogLevel.Information), 31 | It.Is(eventId => eventId.Id == 0), 32 | It.Is((@object, @type) => @object.ToString() == "Hello World Cron!" && @type.Name == "FormattedLogValues"), 33 | It.IsAny(), 34 | It.IsAny>()), 35 | Times.Once); 36 | loggerMock.Verify(logger => logger.Log( 37 | It.Is(logLevel => logLevel == LogLevel.Debug), 38 | It.Is(eventId => eventId.Id == 0), 39 | It.Is((@object, @type) => @object.ToString() == "Finished Run Cron" && @type.Name == "FormattedLogValues"), 40 | It.IsAny(), 41 | It.IsAny>()), 42 | Times.Once); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/Services/ProcessDataTest.cs: -------------------------------------------------------------------------------- 1 | using Autofac.Extras.Moq; 2 | using BervProject.WebApi.Boilerplate.Services; 3 | using Microsoft.Extensions.Logging; 4 | using Moq; 5 | using System; 6 | using Xunit; 7 | 8 | namespace BervProject.WebApi.Test.Services 9 | { 10 | public class ProcessDataTest 11 | { 12 | 13 | [Fact] 14 | public void ProcessDataSuccess() 15 | { 16 | using var mock = AutoMock.GetLoose(); 17 | var loggerMock = mock.Mock>(); 18 | var processData = mock.Create(); 19 | processData.Process("Hello!"); 20 | loggerMock.Verify(logger => logger.Log( 21 | It.Is(logLevel => logLevel == LogLevel.Debug), 22 | It.Is(eventId => eventId.Id == 0), 23 | It.Is((@object, @type) => @object.ToString() == "You get message: Hello!" && @type.Name == "FormattedLogValues"), 24 | It.IsAny(), 25 | It.IsAny>()), 26 | Times.Once); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BervProject.WebApi.Test/WeatherForecastTest.cs: -------------------------------------------------------------------------------- 1 | using Autofac.Extras.Moq; 2 | using BervProject.WebApi.Boilerplate.Controllers; 3 | using Microsoft.Extensions.Caching.Distributed; 4 | using Moq; 5 | using System; 6 | using System.Linq; 7 | using System.Text; 8 | using Xunit; 9 | 10 | namespace BervProject.WebApi.Test 11 | { 12 | public class WeatherForecastTest 13 | { 14 | [Fact] 15 | public void GetTest() 16 | { 17 | using var mock = AutoMock.GetLoose(); 18 | var controller = mock.Create(); 19 | var result = controller.Get(); 20 | Assert.Equal(5, result.Count()); 21 | } 22 | 23 | [Fact] 24 | public void GetCacheTest() 25 | { 26 | using var mock = AutoMock.GetLoose(); 27 | mock.Mock().Setup(x => x.Get(It.IsAny())).Returns((byte[])null); 28 | var cacheMock = mock.Create(); 29 | var controller = mock.Create(); 30 | var result = controller.GetCache(cacheMock); 31 | Assert.Equal("", result.Value); 32 | } 33 | 34 | [Fact] 35 | public void GetCacheResultTest() 36 | { 37 | using var mock = AutoMock.GetLoose(); 38 | const string expectedResult = "I know anything"; 39 | var expectedByte = Encoding.ASCII.GetBytes(expectedResult); 40 | mock.Mock().Setup(x => x.Get(It.IsAny())).Returns(expectedByte); 41 | var cacheMock = mock.Create(); 42 | var controller = mock.Create(); 43 | var result = controller.GetCache(cacheMock); 44 | Assert.Equal(expectedResult, result.Value); 45 | } 46 | 47 | [Fact] 48 | public void TriggerExceptionTest() 49 | { 50 | using var mock = AutoMock.GetLoose(); 51 | var controller = mock.Create(); 52 | var result = Assert.Throws(() => controller.TriggerException()); 53 | Assert.Equal("Unhandled Exception", result.Message); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine as build 2 | WORKDIR /app 3 | COPY . . 4 | RUN dotnet restore 5 | RUN dotnet publish BervProject.WebApi.Boilerplate -c Release -o /app/publish 6 | 7 | FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine as runtime 8 | COPY --from=build /app/publish /app/publish 9 | WORKDIR /app/publish 10 | EXPOSE 80 11 | CMD ["dotnet", "BervProject.WebApi.Boilerplate.dll"] 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bervianto Leo Pratama's Personal Projects 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NET Core API Boilerplate (BervProject.WebApi.Boilerplate) 2 | 3 | Net Core Web API Boilerplate for My Project 4 | 5 | ## .NET Version 6 | 7 | 8.0 8 | 9 | ## Build Status 10 | 11 | | Github Action (Build) | Github Action (Docker) | Azure Pipelines | Codecov | 12 | |:---:|:---:|:---:|:---:| 13 | | [![Build .NET](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/build-and-dockerize.yml/badge.svg)](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/build-and-dockerize.yml) | [![Dockerize](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/docker.yml/badge.svg)](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/docker.yml) | [![Build Status](https://dev.azure.com/berviantoleo/NETCoreWebAPIBoilerplate/_apis/build/status/bervProject.NETCoreAPIBoilerplate?branchName=main)](https://dev.azure.com/berviantoleo/NETCoreWebAPIBoilerplate/_build/latest?definitionId=6&branchName=main) | [![codecov](https://codecov.io/gh/bervProject/NETCoreAPIBoilerplate/branch/main/graph/badge.svg?token=4OP6CFN2PX)](https://codecov.io/gh/bervProject/NETCoreAPIBoilerplate) | 14 | 15 | ## LICENSE 16 | 17 | MIT 18 | 19 | ```markdown 20 | MIT License 21 | 22 | Copyright (c) 2019 Bervianto Leo Pratama's Personal Projects 23 | 24 | Permission is hereby granted, free of charge, to any person obtaining a copy 25 | of this software and associated documentation files (the "Software"), to deal 26 | in the Software without restriction, including without limitation the rights 27 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | copies of the Software, and to permit persons to whom the Software is 29 | furnished to do so, subject to the following conditions: 30 | 31 | The above copyright notice and this permission notice shall be included in all 32 | copies or substantial portions of the Software. 33 | 34 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | SOFTWARE. 41 | ``` 42 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET Core 2 | # Build and test ASP.NET Core projects targeting .NET Core. 3 | # Add steps that run tests, create a NuGet package, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core 5 | pr: 6 | - main 7 | 8 | trigger: 9 | - main 10 | 11 | pool: 12 | vmImage: 'ubuntu-24.04' 13 | 14 | resources: 15 | containers: 16 | - container: postgres 17 | image: postgres:17-alpine 18 | env: 19 | POSTGRES_PASSWORD: postgres 20 | POSTGRES_DB: testdb 21 | ports: 22 | - 5432:5432 23 | - container: azurite 24 | image: mcr.microsoft.com/azure-storage/azurite 25 | ports: 26 | - 10000:10000 27 | - 10001:10001 28 | - 10002:10002 29 | 30 | variables: 31 | buildConfiguration: 'Release' 32 | imageName: 'berviantoleo/net-core-boilerplate' 33 | dotnetSdkVersion: '9.0.x' 34 | postgresConnectionString: 'Host=localhost;Database=testdb;Username=postgres;Password=postgres' 35 | 36 | jobs: 37 | - job: "BuildTest" 38 | services: 39 | postgres: postgres 40 | azurite: azurite 41 | steps: 42 | - task: UseDotNet@2 43 | displayName: 'Use .NET Core SDK $(dotnetSdkVersion)' 44 | inputs: 45 | version: '$(dotnetSdkVersion)' 46 | includePreviewVersions: true 47 | - task: DotNetCoreCLI@2 48 | displayName: 'Restore project dependencies' 49 | inputs: 50 | command: 'restore' 51 | projects: '**/*.csproj' 52 | - task: DotNetCoreCLI@2 53 | displayName: 'Build the project - $(buildConfiguration)' 54 | inputs: 55 | command: 'build' 56 | arguments: '--no-restore --configuration $(buildConfiguration)' 57 | projects: '**/*.csproj' 58 | - script: | 59 | dotnet tool install --global dotnet-ef 60 | dotnet tool restore 61 | dotnet ef database update --startup-project BervProject.WebApi.Boilerplate --project BervProject.WebApi.Boilerplate 62 | displayName: 'Migrate DB' 63 | env: 64 | ConnectionStrings__BoilerplateConnectionString: $(postgresConnectionString) 65 | - task: DotNetCoreCLI@2 66 | displayName: 'Run unit tests - $(buildConfiguration)' 67 | inputs: 68 | command: 'test' 69 | arguments: '--no-build --configuration $(buildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(Build.SourcesDirectory)/TestResults/Coverage/' 70 | publishTestResults: true 71 | projects: '**/*.Test.csproj' 72 | env: 73 | ConnectionStrings__BoilerplateConnectionString: $(postgresConnectionString) 74 | - task: PublishCodeCoverageResults@2 75 | displayName: 'Publish code coverage report' 76 | inputs: 77 | codeCoverageTool: 'cobertura' 78 | summaryFileLocation: '$(Build.SourcesDirectory)/**/coverage.cobertura.xml' 79 | - script: curl -Os https://uploader.codecov.io/latest/linux/codecov && chmod +x codecov && ./codecov 80 | displayName: 'Publish to Codecov' 81 | - task: DotNetCoreCLI@2 82 | displayName: 'Publish the project - $(buildConfiguration)' 83 | inputs: 84 | command: 'publish' 85 | projects: '**/BervProject.WebApi.Boilerplate.csproj' 86 | publishWebProjects: false 87 | arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)' 88 | zipAfterPublish: true 89 | - task: PublishBuildArtifacts@1 90 | displayName: 'Publish Artifact: drop' 91 | condition: succeeded() 92 | 93 | - job: "Docker" 94 | steps: 95 | - task: Docker@2 96 | displayName: Build an image 97 | inputs: 98 | repository: $(imageName) 99 | command: build 100 | Dockerfile: Dockerfile 101 | 102 | - job: "CodeQuality" 103 | pool: 104 | vmImage: "windows-2022" 105 | steps: 106 | - task: UseDotNet@2 107 | displayName: 'Use .NET Core SDK $(dotnetSdkVersion)' 108 | inputs: 109 | version: '$(dotnetSdkVersion)' 110 | includePreviewVersions: true 111 | - task: DotNetCoreCLI@2 112 | displayName: 'Restore project dependencies' 113 | inputs: 114 | command: 'restore' 115 | projects: '**/*.csproj' 116 | - task: DotNetCoreCLI@2 117 | displayName: 'Build the project - $(buildConfiguration)' 118 | inputs: 119 | command: 'build' 120 | arguments: '--no-restore --configuration $(buildConfiguration)' 121 | projects: '**/*.csproj' 122 | - task: DotNetCoreCLI@2 123 | displayName: 'Run unit tests - $(buildConfiguration)' 124 | inputs: 125 | command: 'test' 126 | arguments: '--no-build --configuration $(buildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(Build.SourcesDirectory)/TestResults/Coverage/' 127 | publishTestResults: true 128 | projects: | 129 | 'BervProject.WebApi.Test/BervProject.WebApi.Test.csproj' 130 | env: 131 | ConnectionStrings__BoilerplateConnectionString: $(postgresConnectionString) 132 | - task: NDependTask@1 133 | env: 134 | SYSTEM_ACCESSTOKEN: $(system.accesstoken) 135 | 136 | --------------------------------------------------------------------------------