diff --git a/.dockerignore b/.dockerignore index 96901033d..d3c02c94e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,9 +1,9 @@ -**/bin/ -**/obj/ -**/*.user - -.*/ - -db -test -src/HappyCode.NetCoreBoilerplate.Db +**/bin/ +**/obj/ +**/*.user + +.*/ + +db +test +src/HappyCode.NetCoreBoilerplate.Db diff --git a/.editorconfig b/.editorconfig index c4d9fb779..2468ca8f9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,28 +1,28 @@ -; Top-most EditorConfig file -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.{bat,cmd}] -insert_final_newline = false - -[*.cs] -indent_size = 4 - -dotnet_sort_system_directives_first = true - -# private fields with underscore prefix => private int _myValue; -dotnet_naming_rule.private_members_with_underscore.symbols = private_fields -dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore -dotnet_naming_rule.private_members_with_underscore.severity = suggestion - -dotnet_naming_symbols.private_fields.applicable_kinds = field -dotnet_naming_symbols.private_fields.applicable_accessibilities = private - -dotnet_naming_style.prefix_underscore.capitalization = camel_case -dotnet_naming_style.prefix_underscore.required_prefix = _ +; Top-most EditorConfig file +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{bat,cmd}] +insert_final_newline = false + +[*.cs] +indent_size = 4 + +dotnet_sort_system_directives_first = true + +# private fields with underscore prefix => private int _myValue; +dotnet_naming_rule.private_members_with_underscore.symbols = private_fields +dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore +dotnet_naming_rule.private_members_with_underscore.severity = suggestion + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 396fee241..ba2fac290 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,10 +1,10 @@ -# Contribution - -You are very welcome to submit either issues or pull requests to this repository! - -For pull request please follow this rules: - -* Commit messages should be clear and as much as possible descriptive. -* Rebase if required. -* Make sure that your code compile and run locally. -* Changes do not break any tests and code quality rules. +# Contribution + +You are very welcome to submit either issues or pull requests to this repository! + +For pull request please follow this rules: + +* Commit messages should be clear and as much as possible descriptive. +* Rebase if required. +* Make sure that your code compile and run locally. +* Changes do not break any tests and code quality rules. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index ed7f63da6..7a6298c5b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ ---- -custom: ["https://www.buymeacoffee.com/lkurzyniec"] +--- +custom: ["https://www.buymeacoffee.com/lkurzyniec"] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5dc96b752..451e1b0a1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,11 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "monthly" - - - package-ecosystem: "nuget" - directory: "/" - schedule: - interval: "weekly" +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..7f2e66278 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,49 @@ +--- +name: Docker build + +on: + push: + paths-ignore: + - '**.md' + workflow_dispatch: + workflow_call: + +env: + BASE_TAG: lkurzyniec/netcore-boilerplate + +jobs: + build-and-test: + name: Build and Test + runs-on: ubuntu-latest + + steps: + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build docker image + uses: docker/build-push-action@v5 + with: + load: true + tags: ${{ env.BASE_TAG }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Test container + run: | + docker run -d --rm -p 5000:8080 ${{ env.BASE_TAG }}:latest + + echo "Taking a short nap..." + sleep 5s + + echo "Checking health check endpoint..." + curl -Is http://localhost:5000/health | head -n 1 + HTTP_CODE=$(curl -o /dev/null -s -w "%{http_code}\n" http://localhost:5000/health) + echo "HTTP_CODE=$HTTP_CODE" >> $GITHUB_OUTPUT + + # echo "Validating response..." + # if [ "$HTTP_CODE" = "200" ]; then + # echo "All is valid!" + # else + # echo "Response is invalid!" + # exit 1 + # fi diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml new file mode 100644 index 000000000..67abd4c97 --- /dev/null +++ b/.github/workflows/docker-push.yml @@ -0,0 +1,53 @@ +--- +name: Docker push + +on: + release: + types: [published] + +env: + BASE_TAG: lkurzyniec/netcore-boilerplate + +jobs: + build-and-test: + name: Build and Test + uses: ./.github/workflows/docker-build.yml + + push: + name: Push to Docker Hub + runs-on: ubuntu-latest + needs: build-and-test + environment: docker + concurrency: docker + if: | + github.event_name == 'release' && + github.event.action == 'published' && + github.ref_type == 'tag' + # && needs.build-and-test.outputs.HTTP_CODE == 200 + + steps: + - name: Grab a version + run: | + version="{{ github.ref }}" + version="${version/refs\/tags\/v/}" + + echo "Version: ${version}" + echo "### Version ${version} published! :rocket:" >> $GITHUB_STEP_SUMMARY + echo "VERSION=${version}" >> $GITHUB_ENV + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + push: true + tags: | + ${{ env.BASE_TAG }}:latest + ${{ env.BASE_TAG }}:${{ env.VERSION }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 58b7b8cb7..000000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Build docker image - -on: - push: - paths-ignore: - - '**.md' - workflow_dispatch: - -jobs: - build-image: - name: Build docker image - runs-on: ubuntu-latest - - steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build docker image - uses: docker/build-push-action@v5 - with: - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 34723fe5c..1d11551ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,320 +1,320 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -dist/ -*.db - -# Git files -*.orig -*.bak - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio cache/options directory -.vs/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# ApprovalTests.Net -*.received.* - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a Code Coverage Tool -coverage.json -coverage.info -coverage*.xml - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Typescript v1 declaration files -typings/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Local config overrides -appsettings.local.json +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +dist/ +*.db + +# Git files +*.orig +*.bak + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# ApprovalTests.Net +*.received.* + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a Code Coverage Tool +coverage.json +coverage.info +coverage*.xml + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Local config overrides +appsettings.local.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 1efbdf2f7..11b7d2e2d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,26 +1,26 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch API", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/bin/Debug/netcoreapp3.1/HappyCode.NetCoreBoilerplate.Api.dll", - "args": [], - "cwd": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api", - "stopAtEntry": false, - "serverReadyAction": { - "action": "openExternally", - "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)", - }, - "env": { - "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "http://*:5000", - } - } - ] -} +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch API", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/bin/Debug/netcoreapp3.1/HappyCode.NetCoreBoilerplate.Api.dll", + "args": [], + "cwd": "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)", + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://*:5000", + } + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f1fc58100..fb2c872e1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,17 +1,15 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/HappyCode.NetCoreBoilerplate.Api.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - } - ] -} +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/HappyCode.NetCoreBoilerplate.Api/HappyCode.NetCoreBoilerplate.Api.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/.whitesource b/.whitesource index e0aaa3e9e..5886d62bb 100644 --- a/.whitesource +++ b/.whitesource @@ -1,8 +1,8 @@ -{ - "checkRunSettings": { - "vulnerableCheckRunConclusionLevel": "failure" - }, - "issueSettings": { - "minSeverityLevel": "LOW" - } +{ + "checkRunSettings": { + "vulnerableCheckRunConclusionLevel": "failure" + }, + "issueSettings": { + "minSeverityLevel": "LOW" + } } \ No newline at end of file diff --git a/HappyCode.NetCoreBoilerplate.ruleset b/HappyCode.NetCoreBoilerplate.ruleset index c58845891..b0dd81e79 100644 --- a/HappyCode.NetCoreBoilerplate.ruleset +++ b/HappyCode.NetCoreBoilerplate.ruleset @@ -1,18 +1,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HappyCode.NetCoreBoilerplate.sln b/HappyCode.NetCoreBoilerplate.sln index 576897e3e..5ea154a5e 100644 --- a/HappyCode.NetCoreBoilerplate.sln +++ b/HappyCode.NetCoreBoilerplate.sln @@ -1,106 +1,106 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.32014.148 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6D3497A0-339F-4545-86B8-7E015B468025}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Api", "src\HappyCode.NetCoreBoilerplate.Api\HappyCode.NetCoreBoilerplate.Api.csproj", "{C29597BD-2BE6-4E35-B6BF-E3D416AE3661}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Core", "src\HappyCode.NetCoreBoilerplate.Core\HappyCode.NetCoreBoilerplate.Core.csproj", "{C658A533-24EC-4586-81E7-C51D2F217945}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Api.IntegrationTests", "test\HappyCode.NetCoreBoilerplate.Api.IntegrationTests\HappyCode.NetCoreBoilerplate.Api.IntegrationTests.csproj", "{89F377EB-45A8-404D-861D-78B41B73C19D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Api.UnitTests", "test\HappyCode.NetCoreBoilerplate.Api.UnitTests\HappyCode.NetCoreBoilerplate.Api.UnitTests.csproj", "{1606FA62-EA72-4F99-971C-0DA67D910188}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Core.UnitTests", "test\HappyCode.NetCoreBoilerplate.Core.UnitTests\HappyCode.NetCoreBoilerplate.Core.UnitTests.csproj", "{E7FD0B44-8ACA-4EA6-8A38-E6D346462273}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A9D8D97C-B58E-4A12-A7FE-D93A71CFFB73}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - Directory.Build.props = Directory.Build.props - Directory.Packages.props = Directory.Packages.props - docker-compose.yml = docker-compose.yml - dockerfile = dockerfile - HappyCode.NetCoreBoilerplate.ruleset = HappyCode.NetCoreBoilerplate.ruleset - README.md = README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Db", "src\HappyCode.NetCoreBoilerplate.Db\HappyCode.NetCoreBoilerplate.Db.csproj", "{AE04301F-4E74-48DA-BFF9-D879618DA124}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ci", "ci", "{C20F30D0-1383-4834-805F-6526300F4D2D}" - ProjectSection(SolutionItems) = preProject - .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml - .github\workflows\docker.yml = .github\workflows\docker.yml - .github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.ArchitecturalTests", "test\HappyCode.NetCoreBoilerplate.ArchitecturalTests\HappyCode.NetCoreBoilerplate.ArchitecturalTests.csproj", "{C8166147-F446-4774-AFEC-9C1795B822B8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.BooksModule", "src\HappyCode.NetCoreBoilerplate.BooksModule\HappyCode.NetCoreBoilerplate.BooksModule.csproj", "{75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests", "test\HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests\HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests.csproj", "{B3E52219-7B85-42B3-B607-F323401AC878}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Release|Any CPU.Build.0 = Release|Any CPU - {C658A533-24EC-4586-81E7-C51D2F217945}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C658A533-24EC-4586-81E7-C51D2F217945}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C658A533-24EC-4586-81E7-C51D2F217945}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C658A533-24EC-4586-81E7-C51D2F217945}.Release|Any CPU.Build.0 = Release|Any CPU - {89F377EB-45A8-404D-861D-78B41B73C19D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89F377EB-45A8-404D-861D-78B41B73C19D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89F377EB-45A8-404D-861D-78B41B73C19D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89F377EB-45A8-404D-861D-78B41B73C19D}.Release|Any CPU.Build.0 = Release|Any CPU - {1606FA62-EA72-4F99-971C-0DA67D910188}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1606FA62-EA72-4F99-971C-0DA67D910188}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1606FA62-EA72-4F99-971C-0DA67D910188}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1606FA62-EA72-4F99-971C-0DA67D910188}.Release|Any CPU.Build.0 = Release|Any CPU - {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Release|Any CPU.Build.0 = Release|Any CPU - {AE04301F-4E74-48DA-BFF9-D879618DA124}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AE04301F-4E74-48DA-BFF9-D879618DA124}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE04301F-4E74-48DA-BFF9-D879618DA124}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AE04301F-4E74-48DA-BFF9-D879618DA124}.Release|Any CPU.Build.0 = Release|Any CPU - {C8166147-F446-4774-AFEC-9C1795B822B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C8166147-F446-4774-AFEC-9C1795B822B8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C8166147-F446-4774-AFEC-9C1795B822B8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C8166147-F446-4774-AFEC-9C1795B822B8}.Release|Any CPU.Build.0 = Release|Any CPU - {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Release|Any CPU.Build.0 = Release|Any CPU - {B3E52219-7B85-42B3-B607-F323401AC878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B3E52219-7B85-42B3-B607-F323401AC878}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B3E52219-7B85-42B3-B607-F323401AC878}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B3E52219-7B85-42B3-B607-F323401AC878}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {C29597BD-2BE6-4E35-B6BF-E3D416AE3661} = {6D3497A0-339F-4545-86B8-7E015B468025} - {C658A533-24EC-4586-81E7-C51D2F217945} = {6D3497A0-339F-4545-86B8-7E015B468025} - {89F377EB-45A8-404D-861D-78B41B73C19D} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} - {1606FA62-EA72-4F99-971C-0DA67D910188} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} - {E7FD0B44-8ACA-4EA6-8A38-E6D346462273} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} - {AE04301F-4E74-48DA-BFF9-D879618DA124} = {6D3497A0-339F-4545-86B8-7E015B468025} - {C8166147-F446-4774-AFEC-9C1795B822B8} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} - {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8} = {6D3497A0-339F-4545-86B8-7E015B468025} - {B3E52219-7B85-42B3-B607-F323401AC878} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FE6D8568-233C-4A49-84FF-2FE6C0DE238C} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6D3497A0-339F-4545-86B8-7E015B468025}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Api", "src\HappyCode.NetCoreBoilerplate.Api\HappyCode.NetCoreBoilerplate.Api.csproj", "{C29597BD-2BE6-4E35-B6BF-E3D416AE3661}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Core", "src\HappyCode.NetCoreBoilerplate.Core\HappyCode.NetCoreBoilerplate.Core.csproj", "{C658A533-24EC-4586-81E7-C51D2F217945}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Api.IntegrationTests", "test\HappyCode.NetCoreBoilerplate.Api.IntegrationTests\HappyCode.NetCoreBoilerplate.Api.IntegrationTests.csproj", "{89F377EB-45A8-404D-861D-78B41B73C19D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Api.UnitTests", "test\HappyCode.NetCoreBoilerplate.Api.UnitTests\HappyCode.NetCoreBoilerplate.Api.UnitTests.csproj", "{1606FA62-EA72-4F99-971C-0DA67D910188}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Core.UnitTests", "test\HappyCode.NetCoreBoilerplate.Core.UnitTests\HappyCode.NetCoreBoilerplate.Core.UnitTests.csproj", "{E7FD0B44-8ACA-4EA6-8A38-E6D346462273}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A9D8D97C-B58E-4A12-A7FE-D93A71CFFB73}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props + docker-compose.yml = docker-compose.yml + dockerfile = dockerfile + HappyCode.NetCoreBoilerplate.ruleset = HappyCode.NetCoreBoilerplate.ruleset + README.md = README.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.Db", "src\HappyCode.NetCoreBoilerplate.Db\HappyCode.NetCoreBoilerplate.Db.csproj", "{AE04301F-4E74-48DA-BFF9-D879618DA124}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ci", "ci", "{C20F30D0-1383-4834-805F-6526300F4D2D}" + ProjectSection(SolutionItems) = preProject + .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml + .github\workflows\docker.yml = .github\workflows\docker.yml + .github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.ArchitecturalTests", "test\HappyCode.NetCoreBoilerplate.ArchitecturalTests\HappyCode.NetCoreBoilerplate.ArchitecturalTests.csproj", "{C8166147-F446-4774-AFEC-9C1795B822B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.BooksModule", "src\HappyCode.NetCoreBoilerplate.BooksModule\HappyCode.NetCoreBoilerplate.BooksModule.csproj", "{75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests", "test\HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests\HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests.csproj", "{B3E52219-7B85-42B3-B607-F323401AC878}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C29597BD-2BE6-4E35-B6BF-E3D416AE3661}.Release|Any CPU.Build.0 = Release|Any CPU + {C658A533-24EC-4586-81E7-C51D2F217945}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C658A533-24EC-4586-81E7-C51D2F217945}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C658A533-24EC-4586-81E7-C51D2F217945}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C658A533-24EC-4586-81E7-C51D2F217945}.Release|Any CPU.Build.0 = Release|Any CPU + {89F377EB-45A8-404D-861D-78B41B73C19D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89F377EB-45A8-404D-861D-78B41B73C19D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89F377EB-45A8-404D-861D-78B41B73C19D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89F377EB-45A8-404D-861D-78B41B73C19D}.Release|Any CPU.Build.0 = Release|Any CPU + {1606FA62-EA72-4F99-971C-0DA67D910188}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1606FA62-EA72-4F99-971C-0DA67D910188}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1606FA62-EA72-4F99-971C-0DA67D910188}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1606FA62-EA72-4F99-971C-0DA67D910188}.Release|Any CPU.Build.0 = Release|Any CPU + {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7FD0B44-8ACA-4EA6-8A38-E6D346462273}.Release|Any CPU.Build.0 = Release|Any CPU + {AE04301F-4E74-48DA-BFF9-D879618DA124}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE04301F-4E74-48DA-BFF9-D879618DA124}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE04301F-4E74-48DA-BFF9-D879618DA124}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE04301F-4E74-48DA-BFF9-D879618DA124}.Release|Any CPU.Build.0 = Release|Any CPU + {C8166147-F446-4774-AFEC-9C1795B822B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8166147-F446-4774-AFEC-9C1795B822B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8166147-F446-4774-AFEC-9C1795B822B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8166147-F446-4774-AFEC-9C1795B822B8}.Release|Any CPU.Build.0 = Release|Any CPU + {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8}.Release|Any CPU.Build.0 = Release|Any CPU + {B3E52219-7B85-42B3-B607-F323401AC878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3E52219-7B85-42B3-B607-F323401AC878}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3E52219-7B85-42B3-B607-F323401AC878}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3E52219-7B85-42B3-B607-F323401AC878}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {C29597BD-2BE6-4E35-B6BF-E3D416AE3661} = {6D3497A0-339F-4545-86B8-7E015B468025} + {C658A533-24EC-4586-81E7-C51D2F217945} = {6D3497A0-339F-4545-86B8-7E015B468025} + {89F377EB-45A8-404D-861D-78B41B73C19D} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} + {1606FA62-EA72-4F99-971C-0DA67D910188} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} + {E7FD0B44-8ACA-4EA6-8A38-E6D346462273} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} + {AE04301F-4E74-48DA-BFF9-D879618DA124} = {6D3497A0-339F-4545-86B8-7E015B468025} + {C8166147-F446-4774-AFEC-9C1795B822B8} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} + {75AF49EF-1411-408A-B9E7-CE8CADAB7FC8} = {6D3497A0-339F-4545-86B8-7E015B468025} + {B3E52219-7B85-42B3-B607-F323401AC878} = {8AC3899F-4C0B-4EF6-AD2B-97D91FE107F5} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FE6D8568-233C-4A49-84FF-2FE6C0DE238C} + EndGlobalSection +EndGlobal diff --git a/LICENSE b/LICENSE index d159169d1..89e08fb00 100644 --- a/LICENSE +++ b/LICENSE @@ -1,339 +1,339 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/codecov.yml b/codecov.yml index f20b80b96..5dd0f5040 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,5 +1,5 @@ ---- -coverage: - precision: 0 - round: up - range: "30...100" +--- +coverage: + precision: 0 + round: up + range: "30...100" diff --git a/db/mysql/mysql-employees.sql b/db/mysql/mysql-employees.sql index d2cb72079..62a53346b 100644 --- a/db/mysql/mysql-employees.sql +++ b/db/mysql/mysql-employees.sql @@ -1,86 +1,86 @@ --- Sample employee database --- See changelog table for details --- Copyright (C) 2007,2008, MySQL AB --- --- Original data created by Fusheng Wang and Carlo Zaniolo --- http://www.cs.aau.dk/TimeCenter/software.htm --- http://www.cs.aau.dk/TimeCenter/Data/employeeTemporalDataSet.zip --- --- Current schema by Giuseppe Maxia --- Data conversion from XML to relational by Patrick Crews --- --- This work is licensed under the --- Creative Commons Attribution-Share Alike 3.0 Unported License. --- To view a copy of this license, visit --- http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to --- Creative Commons, 171 Second Street, Suite 300, San Francisco, --- California, 94105, USA. --- --- DISCLAIMER --- To the best of our knowledge, this data is fabricated, and --- it does not correspond to real people. --- Any similarity to existing people is purely coincidental. --- - -DROP DATABASE IF EXISTS employees; -CREATE DATABASE IF NOT EXISTS employees; -USE employees; - -SELECT 'CREATING DATABASE STRUCTURE' as 'INFO'; - -DROP TABLE IF EXISTS employees, - departments; - -/*!50503 set default_storage_engine = InnoDB */; -/*!50503 select CONCAT('storage engine: ', @@default_storage_engine) as INFO */; - -CREATE TABLE employees ( - emp_no INT NOT NULL AUTO_INCREMENT, - birth_date DATE NOT NULL, - first_name VARCHAR(14) NOT NULL, - last_name VARCHAR(16) NOT NULL, - gender ENUM ('M','F') NOT NULL, - PRIMARY KEY (emp_no) -); - -INSERT INTO employees (birth_date, first_name, last_name, gender) VALUES - ('1972-05-13','Peter','Diaz','M'), - ('1987-09-25','Leon','Leonard','M'), - ('1974-05-10','Shirley','Baker','F'), - ('1986-08-17','David','Allen','M'), - ('1959-10-14','Nancy','Davis','F'), - ('1964-07-05','Michael','Wray','M'), - ('1980-10-08','Wanda','Inniss','F'); - -CREATE TABLE departments ( - dept_no CHAR(4) NOT NULL, - dept_name VARCHAR(40) NOT NULL, - manger_no INT NOT NULL, - PRIMARY KEY (dept_no), - UNIQUE KEY (dept_name), - FOREIGN KEY (manger_no) REFERENCES employees (emp_no) ON DELETE CASCADE -); - -INSERT INTO departments VALUES - ('d001','Marketing', 3), - ('d002','Finance', 1), - ('d003','Human Resources', 7); - -ALTER TABLE employees - ADD COLUMN dept_no CHAR(4), - ADD FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE; - -UPDATE employees - SET dept_no = 'd001' - WHERE emp_no IN (3); -UPDATE employees - SET dept_no = 'd002' - WHERE emp_no IN (1, 2); -UPDATE employees - SET dept_no = 'd003' - WHERE emp_no IN (4, 5, 6, 7); - -CREATE USER 'user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'simplepwd'; -GRANT ALL ON employees.* TO 'user'@'localhost'; - -flush /*!50503 binary */ logs; +-- Sample employee database +-- See changelog table for details +-- Copyright (C) 2007,2008, MySQL AB +-- +-- Original data created by Fusheng Wang and Carlo Zaniolo +-- http://www.cs.aau.dk/TimeCenter/software.htm +-- http://www.cs.aau.dk/TimeCenter/Data/employeeTemporalDataSet.zip +-- +-- Current schema by Giuseppe Maxia +-- Data conversion from XML to relational by Patrick Crews +-- +-- This work is licensed under the +-- Creative Commons Attribution-Share Alike 3.0 Unported License. +-- To view a copy of this license, visit +-- http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to +-- Creative Commons, 171 Second Street, Suite 300, San Francisco, +-- California, 94105, USA. +-- +-- DISCLAIMER +-- To the best of our knowledge, this data is fabricated, and +-- it does not correspond to real people. +-- Any similarity to existing people is purely coincidental. +-- + +DROP DATABASE IF EXISTS employees; +CREATE DATABASE IF NOT EXISTS employees; +USE employees; + +SELECT 'CREATING DATABASE STRUCTURE' as 'INFO'; + +DROP TABLE IF EXISTS employees, + departments; + +/*!50503 set default_storage_engine = InnoDB */; +/*!50503 select CONCAT('storage engine: ', @@default_storage_engine) as INFO */; + +CREATE TABLE employees ( + emp_no INT NOT NULL AUTO_INCREMENT, + birth_date DATE NOT NULL, + first_name VARCHAR(14) NOT NULL, + last_name VARCHAR(16) NOT NULL, + gender ENUM ('M','F') NOT NULL, + PRIMARY KEY (emp_no) +); + +INSERT INTO employees (birth_date, first_name, last_name, gender) VALUES + ('1972-05-13','Peter','Diaz','M'), + ('1987-09-25','Leon','Leonard','M'), + ('1974-05-10','Shirley','Baker','F'), + ('1986-08-17','David','Allen','M'), + ('1959-10-14','Nancy','Davis','F'), + ('1964-07-05','Michael','Wray','M'), + ('1980-10-08','Wanda','Inniss','F'); + +CREATE TABLE departments ( + dept_no CHAR(4) NOT NULL, + dept_name VARCHAR(40) NOT NULL, + manger_no INT NOT NULL, + PRIMARY KEY (dept_no), + UNIQUE KEY (dept_name), + FOREIGN KEY (manger_no) REFERENCES employees (emp_no) ON DELETE CASCADE +); + +INSERT INTO departments VALUES + ('d001','Marketing', 3), + ('d002','Finance', 1), + ('d003','Human Resources', 7); + +ALTER TABLE employees + ADD COLUMN dept_no CHAR(4), + ADD FOREIGN KEY (dept_no) REFERENCES departments(dept_no) ON DELETE CASCADE; + +UPDATE employees + SET dept_no = 'd001' + WHERE emp_no IN (3); +UPDATE employees + SET dept_no = 'd002' + WHERE emp_no IN (1, 2); +UPDATE employees + SET dept_no = 'd003' + WHERE emp_no IN (4, 5, 6, 7); + +CREATE USER 'user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'simplepwd'; +GRANT ALL ON employees.* TO 'user'@'localhost'; + +flush /*!50503 binary */ logs; diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Controllers/ApiControllerBase.cs b/src/HappyCode.NetCoreBoilerplate.Api/Controllers/ApiControllerBase.cs index 2b76c485d..2a49bff21 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Controllers/ApiControllerBase.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Controllers/ApiControllerBase.cs @@ -1,11 +1,11 @@ -using Microsoft.AspNetCore.Mvc; - -namespace HappyCode.NetCoreBoilerplate.Api.Controllers -{ - [Produces("application/json")] - [ApiController] - public abstract class ApiControllerBase : ControllerBase - { - - } -} +using Microsoft.AspNetCore.Mvc; + +namespace HappyCode.NetCoreBoilerplate.Api.Controllers +{ + [Produces("application/json")] + [ApiController] + public abstract class ApiControllerBase : ControllerBase + { + + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Controllers/CarsController.cs b/src/HappyCode.NetCoreBoilerplate.Api/Controllers/CarsController.cs index 2a3979cc6..7cb5066c6 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Controllers/CarsController.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Controllers/CarsController.cs @@ -1,42 +1,42 @@ -using HappyCode.NetCoreBoilerplate.Core; -using HappyCode.NetCoreBoilerplate.Core.Dtos; -using HappyCode.NetCoreBoilerplate.Core.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.FeatureManagement.Mvc; - -namespace HappyCode.NetCoreBoilerplate.Api.Controllers -{ - [Route("api/cars")] - public class CarsController : ApiControllerBase - { - private readonly ICarService _carService; - - public CarsController(ICarService carService) - { - _carService = carService; - } - - [HttpGet] - [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task GetAllAsync( - CancellationToken cancellationToken = default) - { - var result = await _carService.GetAllSortedByPlateAsync(cancellationToken); - return Ok(result); - } - - [FeatureGate(FeatureFlags.Santa)] - [HttpGet("santa")] - [ProducesResponseType(typeof(CarDto), StatusCodes.Status200OK)] - public IActionResult GetSantaCar() - { - return Ok(new CarDto - { - Id = int.MaxValue, - Model = "Magic Sleigh", - Plate = "XMas 12", - }); - } - } -} +using HappyCode.NetCoreBoilerplate.Core; +using HappyCode.NetCoreBoilerplate.Core.Dtos; +using HappyCode.NetCoreBoilerplate.Core.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.FeatureManagement.Mvc; + +namespace HappyCode.NetCoreBoilerplate.Api.Controllers +{ + [Route("api/cars")] + public class CarsController : ApiControllerBase + { + private readonly ICarService _carService; + + public CarsController(ICarService carService) + { + _carService = carService; + } + + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAllAsync( + CancellationToken cancellationToken = default) + { + var result = await _carService.GetAllSortedByPlateAsync(cancellationToken); + return Ok(result); + } + + [FeatureGate(FeatureFlags.Santa)] + [HttpGet("santa")] + [ProducesResponseType(typeof(CarDto), StatusCodes.Status200OK)] + public IActionResult GetSantaCar() + { + return Ok(new CarDto + { + Id = int.MaxValue, + Model = "Magic Sleigh", + Plate = "XMas 12", + }); + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Api/ErrorResponse.cs b/src/HappyCode.NetCoreBoilerplate.Api/ErrorResponse.cs index 2990b99bb..cd6473280 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/ErrorResponse.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/ErrorResponse.cs @@ -1,12 +1,12 @@ -using System.Runtime.Serialization; - -namespace HappyCode.NetCoreBoilerplate.Api -{ - public class ErrorResponse - { - public IEnumerable Messages { get; set; } - - [DataMember(EmitDefaultValue = false)] - public string Exception { get; set; } - } -} +using System.Runtime.Serialization; + +namespace HappyCode.NetCoreBoilerplate.Api +{ + public class ErrorResponse + { + public IEnumerable Messages { get; set; } + + [DataMember(EmitDefaultValue = false)] + public string Exception { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index c8b1df1a7..98a809898 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -1,41 +1,41 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters -{ - public class HttpGlobalExceptionFilter : IExceptionFilter - { - private readonly IWebHostEnvironment _env; - private readonly ILogger _logger; - - public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) - { - _env = env; - _logger = logger; - } - - public void OnException(ExceptionContext context) - { - _logger.LogError(new EventId(context.Exception.HResult), - context.Exception, - context.Exception.Message); - - var jsonErrorResponse = new ErrorResponse - { - Messages = new[] { "An internal error has occurred" } - }; - - if (_env.IsDevelopment()) - { - jsonErrorResponse.Exception = context.Exception.ToString(); - } - - context.Result = new ObjectResult(jsonErrorResponse) { StatusCode = StatusCodes.Status500InternalServerError }; - context.ExceptionHandled = true; - } - } -} +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters +{ + public class HttpGlobalExceptionFilter : IExceptionFilter + { + private readonly IWebHostEnvironment _env; + private readonly ILogger _logger; + + public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) + { + _env = env; + _logger = logger; + } + + public void OnException(ExceptionContext context) + { + _logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); + + var jsonErrorResponse = new ErrorResponse + { + Messages = new[] { "An internal error has occurred" } + }; + + if (_env.IsDevelopment()) + { + jsonErrorResponse.Exception = context.Exception.ToString(); + } + + context.Result = new ObjectResult(jsonErrorResponse) { StatusCode = StatusCodes.Status500InternalServerError }; + context.ExceptionHandled = true; + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/ValidateModelStateFilter.cs b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/ValidateModelStateFilter.cs index f4ffdc706..8c0167059 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/ValidateModelStateFilter.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Infrastructure/Filters/ValidateModelStateFilter.cs @@ -1,27 +1,27 @@ -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters -{ - public class ValidateModelStateFilter : ActionFilterAttribute - { - public override void OnActionExecuting(ActionExecutingContext context) - { - if (context.ModelState.IsValid) - { - return; - } - - var validationErrors = context.ModelState - .Keys - .SelectMany(k => context.ModelState[k].Errors) - .Select(e => e.ErrorMessage); - - context.Result = new BadRequestObjectResult(new ErrorResponse - { - Messages = validationErrors - }); - } - } -} +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters +{ + public class ValidateModelStateFilter : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext context) + { + if (context.ModelState.IsValid) + { + return; + } + + var validationErrors = context.ModelState + .Keys + .SelectMany(k => context.ModelState[k].Errors) + .Select(e => e.ErrorMessage); + + context.Result = new BadRequestObjectResult(new ErrorResponse + { + Messages = validationErrors + }); + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Api/Program.cs b/src/HappyCode.NetCoreBoilerplate.Api/Program.cs index ffdf65d2b..7e8131f0f 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/Program.cs +++ b/src/HappyCode.NetCoreBoilerplate.Api/Program.cs @@ -1,31 +1,31 @@ -using HappyCode.NetCoreBoilerplate.Api; -using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; -using Serilog; - -Log.Logger = SerilogConfigurator.CreateLogger(); - -try -{ - Log.Logger.Information("Starting up"); - using var webHost = CreateWebHostBuilder(args).Build(); - await webHost.RunAsync(); -} -catch (Exception ex) -{ - Log.Logger.Fatal(ex, "Application start-up failed"); - throw; -} -finally -{ - Log.CloseAndFlush(); -} - -static IHostBuilder CreateWebHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseSerilog() - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); +using HappyCode.NetCoreBoilerplate.Api; +using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Serilog; + +Log.Logger = SerilogConfigurator.CreateLogger(); + +try +{ + Log.Logger.Information("Starting up"); + using var webHost = CreateWebHostBuilder(args).Build(); + await webHost.RunAsync(); +} +catch (Exception ex) +{ + Log.Logger.Fatal(ex, "Application start-up failed"); + throw; +} +finally +{ + Log.CloseAndFlush(); +} + +static IHostBuilder CreateWebHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSerilog() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); diff --git a/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Development.json b/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Development.json index 95ee6bd8f..92c02fb4f 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Development.json +++ b/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Development.json @@ -1,18 +1,18 @@ -{ - "ConnectionStrings": { - "MySqlDb": "server=localhost;Database=employees;Uid=user;Pwd=simplepwd;", - "MsSqlDb": "Data Source=localhost;Initial Catalog=cars;User ID=user;Password=simplePWD123!" - }, - - "PingWebsite": { - "TimeIntervalInMinutes": 1 - }, - - "FeatureManagement": { - "ApiKey": false - }, - - "Serilog": { - "MinimumLevel": "Debug" - } -} +{ + "ConnectionStrings": { + "MySqlDb": "server=localhost;Database=employees;Uid=user;Pwd=simplepwd;", + "MsSqlDb": "Data Source=localhost;Initial Catalog=cars;User ID=user;Password=simplePWD123!" + }, + + "PingWebsite": { + "TimeIntervalInMinutes": 1 + }, + + "FeatureManagement": { + "ApiKey": false + }, + + "Serilog": { + "MinimumLevel": "Debug" + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Test.json b/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Test.json index efa20169f..51aedfe0b 100644 --- a/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Test.json +++ b/src/HappyCode.NetCoreBoilerplate.Api/appsettings.Test.json @@ -1,5 +1,5 @@ -{ - "FeatureManagement": { - "Santa": false - } -} +{ + "FeatureManagement": { + "Santa": false + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/BooksModuleConfigurations.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/BooksModuleConfigurations.cs index 2135db7cd..a2da8113e 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/BooksModuleConfigurations.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/BooksModuleConfigurations.cs @@ -1,45 +1,45 @@ -using System.Data; -using HappyCode.NetCoreBoilerplate.BooksModule.Features.DeleteBook; -using HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBook; -using HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBooks; -using HappyCode.NetCoreBoilerplate.BooksModule.Features.UpsertBook; -using HappyCode.NetCoreBoilerplate.BooksModule.Infrastructure; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Data.Sqlite; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace HappyCode.NetCoreBoilerplate.BooksModule; - -public static class BooksModuleConfigurations -{ - public static IServiceCollection AddBooksModule(this IServiceCollection services, IConfiguration configuration) - => services - .AddEndpointsApiExplorer() - .AddSingleton(sp => new SqliteConnection(configuration.GetConnectionString("SqliteDb"))) - .AddSingleton() - ; - - public static IEndpointRouteBuilder MapBooksModule(this IEndpointRouteBuilder endpoints) - => endpoints - .MapGroup("/api/books") - .AddEndpointFilter() - .MapGetBooksEndpoint() - .MapGetBookEndpoint() - .MapUpsertBookEndpoint() - .MapDeleteBookEndpoint() - ; - - public static IHealthChecksBuilder AddBooksModule(this IHealthChecksBuilder builder, IConfiguration configuration) - => builder.AddSqlite(configuration.GetConnectionString("SqliteDb")); - - public static IApplicationBuilder InitBooksModule(this IApplicationBuilder app) - { - var initializer = app.ApplicationServices.GetRequiredService(); - initializer.Init(); - - return app; - } -} +using System.Data; +using HappyCode.NetCoreBoilerplate.BooksModule.Features.DeleteBook; +using HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBook; +using HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBooks; +using HappyCode.NetCoreBoilerplate.BooksModule.Features.UpsertBook; +using HappyCode.NetCoreBoilerplate.BooksModule.Infrastructure; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace HappyCode.NetCoreBoilerplate.BooksModule; + +public static class BooksModuleConfigurations +{ + public static IServiceCollection AddBooksModule(this IServiceCollection services, IConfiguration configuration) + => services + .AddEndpointsApiExplorer() + .AddSingleton(sp => new SqliteConnection(configuration.GetConnectionString("SqliteDb"))) + .AddSingleton() + ; + + public static IEndpointRouteBuilder MapBooksModule(this IEndpointRouteBuilder endpoints) + => endpoints + .MapGroup("/api/books") + .AddEndpointFilter() + .MapGetBooksEndpoint() + .MapGetBookEndpoint() + .MapUpsertBookEndpoint() + .MapDeleteBookEndpoint() + ; + + public static IHealthChecksBuilder AddBooksModule(this IHealthChecksBuilder builder, IConfiguration configuration) + => builder.AddSqlite(configuration.GetConnectionString("SqliteDb")); + + public static IApplicationBuilder InitBooksModule(this IApplicationBuilder app) + { + var initializer = app.ApplicationServices.GetRequiredService(); + initializer.Init(); + + return app; + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Dtos/BookDto.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Dtos/BookDto.cs index 9ebc9e2fc..905972b38 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Dtos/BookDto.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Dtos/BookDto.cs @@ -1,3 +1,3 @@ -namespace HappyCode.NetCoreBoilerplate.BooksModule.Dtos; - -public record struct BookDto (int? Id, string Title); +namespace HappyCode.NetCoreBoilerplate.BooksModule.Dtos; + +public record struct BookDto (int? Id, string Title); diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Command.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Command.cs index 814b5ca37..02e84728b 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Command.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Command.cs @@ -1,17 +1,17 @@ -using System.Data; -using Dapper; -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.DeleteBook; - -internal static class Command -{ - private static readonly string _deleteBook = @$" -DELETE FROM Books -WHERE - {nameof(BookDto.Id)} = @id -"; - - public static async Task DeleteBookAsync(this IDbConnection db, int id, CancellationToken cancellationToken) - => await db.ExecuteAsync(new CommandDefinition(_deleteBook, new { id }, cancellationToken: cancellationToken)); -} +using System.Data; +using Dapper; +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.DeleteBook; + +internal static class Command +{ + private static readonly string _deleteBook = @$" +DELETE FROM Books +WHERE + {nameof(BookDto.Id)} = @id +"; + + public static async Task DeleteBookAsync(this IDbConnection db, int id, CancellationToken cancellationToken) + => await db.ExecuteAsync(new CommandDefinition(_deleteBook, new { id }, cancellationToken: cancellationToken)); +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Endpoint.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Endpoint.cs index 5896ed016..5620ae6ae 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Endpoint.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/DeleteBook/Endpoint.cs @@ -1,31 +1,31 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using System.Data; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.DeleteBook; - -internal static class Endpoint -{ - public static IEndpointRouteBuilder MapDeleteBookEndpoint(this IEndpointRouteBuilder endpoints) - { - endpoints - .MapDelete( - "/{id:int}", - async ( - int id, - IDbConnection db, - CancellationToken ct - ) => - { - var affected = await db.DeleteBookAsync(id, ct); - return affected > 0 - ? Results.NoContent() - : Results.NotFound(); - }) - .Produces(StatusCodes.Status204NoContent) - .Produces(StatusCodes.Status404NotFound) - .WithTags("Books"); - return endpoints; - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using System.Data; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.DeleteBook; + +internal static class Endpoint +{ + public static IEndpointRouteBuilder MapDeleteBookEndpoint(this IEndpointRouteBuilder endpoints) + { + endpoints + .MapDelete( + "/{id:int}", + async ( + int id, + IDbConnection db, + CancellationToken ct + ) => + { + var affected = await db.DeleteBookAsync(id, ct); + return affected > 0 + ? Results.NoContent() + : Results.NotFound(); + }) + .Produces(StatusCodes.Status204NoContent) + .Produces(StatusCodes.Status404NotFound) + .WithTags("Books"); + return endpoints; + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Endpoint.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Endpoint.cs index bfb49b814..f40fb7cbc 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Endpoint.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Endpoint.cs @@ -1,32 +1,32 @@ -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using System.Data; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBook; - -internal static class Endpoint -{ - public static IEndpointRouteBuilder MapGetBookEndpoint(this IEndpointRouteBuilder endpoints) - { - endpoints - .MapGet( - "/{id:int}", - async ( - int id, - IDbConnection db, - CancellationToken ct - ) => - { - var book = await db.GetBookAsync(id, ct); - return book.Id is not null - ? Results.Ok(book) - : Results.NotFound(); - }) - .Produces() - .Produces(StatusCodes.Status404NotFound) - .WithTags("Books"); - return endpoints; - } -} +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using System.Data; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBook; + +internal static class Endpoint +{ + public static IEndpointRouteBuilder MapGetBookEndpoint(this IEndpointRouteBuilder endpoints) + { + endpoints + .MapGet( + "/{id:int}", + async ( + int id, + IDbConnection db, + CancellationToken ct + ) => + { + var book = await db.GetBookAsync(id, ct); + return book.Id is not null + ? Results.Ok(book) + : Results.NotFound(); + }) + .Produces() + .Produces(StatusCodes.Status404NotFound) + .WithTags("Books"); + return endpoints; + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Query.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Query.cs index f593997e1..4b87189e2 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Query.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBook/Query.cs @@ -1,21 +1,21 @@ -using System.Data; -using Dapper; -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBook; - -internal static class Query -{ - private static readonly string _getBook = @$" -SELECT - {nameof(BookDto.Id)}, - {nameof(BookDto.Title)} -FROM - Books -WHERE - {nameof(BookDto.Id)} = @id -"; - - public static Task GetBookAsync(this IDbConnection db, int id, CancellationToken cancellationToken) - => db.QuerySingleOrDefaultAsync(new CommandDefinition(_getBook, new { id }, cancellationToken: cancellationToken)); -} +using System.Data; +using Dapper; +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBook; + +internal static class Query +{ + private static readonly string _getBook = @$" +SELECT + {nameof(BookDto.Id)}, + {nameof(BookDto.Title)} +FROM + Books +WHERE + {nameof(BookDto.Id)} = @id +"; + + public static Task GetBookAsync(this IDbConnection db, int id, CancellationToken cancellationToken) + => db.QuerySingleOrDefaultAsync(new CommandDefinition(_getBook, new { id }, cancellationToken: cancellationToken)); +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Endpoint.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Endpoint.cs index 73f9b3250..1717f56a7 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Endpoint.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Endpoint.cs @@ -1,27 +1,27 @@ -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using System.Data; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBooks; - -internal static class Endpoint -{ - public static IEndpointRouteBuilder MapGetBooksEndpoint(this IEndpointRouteBuilder endpoints) - { - endpoints - .MapGet( - "/", - async ( - IDbConnection db, - CancellationToken ct - ) => - { - return Results.Ok(await db.GetBooksAsync(ct)); - }) - .Produces>() - .WithTags("Books"); - return endpoints; - } -} +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using System.Data; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBooks; + +internal static class Endpoint +{ + public static IEndpointRouteBuilder MapGetBooksEndpoint(this IEndpointRouteBuilder endpoints) + { + endpoints + .MapGet( + "/", + async ( + IDbConnection db, + CancellationToken ct + ) => + { + return Results.Ok(await db.GetBooksAsync(ct)); + }) + .Produces>() + .WithTags("Books"); + return endpoints; + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Query.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Query.cs index 8e0a54eb7..d8956ed1d 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Query.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/GetBooks/Query.cs @@ -1,19 +1,19 @@ -using System.Data; -using Dapper; -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBooks; - -internal static class Command -{ - private static readonly string _getBooks = @$" -SELECT - {nameof(BookDto.Id)}, - {nameof(BookDto.Title)} -FROM - Books -"; - - public static Task> GetBooksAsync(this IDbConnection db, CancellationToken cancellationToken) - => db.QueryAsync(new CommandDefinition(_getBooks, cancellationToken: cancellationToken)); -} +using System.Data; +using Dapper; +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.GetBooks; + +internal static class Command +{ + private static readonly string _getBooks = @$" +SELECT + {nameof(BookDto.Id)}, + {nameof(BookDto.Title)} +FROM + Books +"; + + public static Task> GetBooksAsync(this IDbConnection db, CancellationToken cancellationToken) + => db.QueryAsync(new CommandDefinition(_getBooks, cancellationToken: cancellationToken)); +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Command.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Command.cs index d86855821..595268356 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Command.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Command.cs @@ -1,30 +1,30 @@ -using System.Data; -using Dapper; -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.UpsertBook; - -internal static class Command -{ - private static readonly string _upsertBook = @$" -INSERT INTO Books ({nameof(BookDto.Id)}, {nameof(BookDto.Title)}) - VALUES(@id, @title) - ON CONFLICT ({nameof(BookDto.Id)}) DO UPDATE SET {nameof(BookDto.Title)} = excluded.{nameof(BookDto.Title)} -"; - - private static readonly string _nextId = @$" -SELECT IFNULL(MAX({nameof(BookDto.Id)}), 0) + 1 - FROM Books -"; - - public static async Task UpsertBookAsync(this IDbConnection db, BookDto book, CancellationToken cancellationToken) - { - var id = book.Id ?? await db.ExecuteScalarAsync(new CommandDefinition(_nextId, cancellationToken: cancellationToken)); - - await db.ExecuteAsync(new CommandDefinition(_upsertBook, new - { - id = id, - title = book.Title, - }, cancellationToken: cancellationToken)); - } -} +using System.Data; +using Dapper; +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.UpsertBook; + +internal static class Command +{ + private static readonly string _upsertBook = @$" +INSERT INTO Books ({nameof(BookDto.Id)}, {nameof(BookDto.Title)}) + VALUES(@id, @title) + ON CONFLICT ({nameof(BookDto.Id)}) DO UPDATE SET {nameof(BookDto.Title)} = excluded.{nameof(BookDto.Title)} +"; + + private static readonly string _nextId = @$" +SELECT IFNULL(MAX({nameof(BookDto.Id)}), 0) + 1 + FROM Books +"; + + public static async Task UpsertBookAsync(this IDbConnection db, BookDto book, CancellationToken cancellationToken) + { + var id = book.Id ?? await db.ExecuteScalarAsync(new CommandDefinition(_nextId, cancellationToken: cancellationToken)); + + await db.ExecuteAsync(new CommandDefinition(_upsertBook, new + { + id = id, + title = book.Title, + }, cancellationToken: cancellationToken)); + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Endpoint.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Endpoint.cs index 91a1eede9..57b183240 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Endpoint.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Features/UpsertBook/Endpoint.cs @@ -1,29 +1,29 @@ -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using System.Data; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.UpsertBook; - -internal static class Endpoint -{ - public static IEndpointRouteBuilder MapUpsertBookEndpoint(this IEndpointRouteBuilder endpoints) - { - endpoints - .MapPost( - "/", - async ( - BookDto book, - IDbConnection db, - CancellationToken ct - ) => - { - await db.UpsertBookAsync(book, ct); - return Results.NoContent(); - }) - .Produces(StatusCodes.Status204NoContent) - .WithTags("Books"); - return endpoints; - } -} +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using System.Data; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Features.UpsertBook; + +internal static class Endpoint +{ + public static IEndpointRouteBuilder MapUpsertBookEndpoint(this IEndpointRouteBuilder endpoints) + { + endpoints + .MapPost( + "/", + async ( + BookDto book, + IDbConnection db, + CancellationToken ct + ) => + { + await db.UpsertBookAsync(book, ct); + return Results.NoContent(); + }) + .Produces(StatusCodes.Status204NoContent) + .WithTags("Books"); + return endpoints; + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.BooksModule/Infrastructure/DbInitializer.cs b/src/HappyCode.NetCoreBoilerplate.BooksModule/Infrastructure/DbInitializer.cs index a1416778f..4f773bc73 100644 --- a/src/HappyCode.NetCoreBoilerplate.BooksModule/Infrastructure/DbInitializer.cs +++ b/src/HappyCode.NetCoreBoilerplate.BooksModule/Infrastructure/DbInitializer.cs @@ -1,34 +1,34 @@ -using System.Data; -using Dapper; -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.Infrastructure; - -internal class DbInitializer -{ - private static readonly string _createBooks = @$" -CREATE TABLE IF NOT EXISTS Books -( - {nameof(BookDto.Id)} INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - {nameof(BookDto.Title)} TEXT -) -"; - - private readonly IDbConnection _db; - - public DbInitializer(IDbConnection db) - { - _db = db; - } - - public void Init() - { - _db.Execute(_createBooks); - - var count = _db.ExecuteScalar("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name = 'Books'"); - if (count == 0) - { - throw new ApplicationException("SQLite DB not initialized properly"); - } - } -} +using System.Data; +using Dapper; +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.Infrastructure; + +internal class DbInitializer +{ + private static readonly string _createBooks = @$" +CREATE TABLE IF NOT EXISTS Books +( + {nameof(BookDto.Id)} INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + {nameof(BookDto.Title)} TEXT +) +"; + + private readonly IDbConnection _db; + + public DbInitializer(IDbConnection db) + { + _db = db; + } + + public void Init() + { + _db.Execute(_createBooks); + + var count = _db.ExecuteScalar("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name = 'Books'"); + if (count == 0) + { + throw new ApplicationException("SQLite DB not initialized properly"); + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/CarsContext.cs b/src/HappyCode.NetCoreBoilerplate.Core/CarsContext.cs index 49b2cc86c..5b119a4e9 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/CarsContext.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/CarsContext.cs @@ -1,43 +1,43 @@ -using HappyCode.NetCoreBoilerplate.Core.Models; -using Microsoft.EntityFrameworkCore; - -namespace HappyCode.NetCoreBoilerplate.Core -{ - public partial class CarsContext : DbContext - { - public CarsContext(DbContextOptions options) - : base(options) - { - } - - public virtual DbSet Cars { get; set; } - public virtual DbSet Owners { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity(entity => - { - entity.Property(e => e.Model).IsUnicode(false); - - entity.Property(e => e.Plate).IsUnicode(false); - - entity.HasOne(d => d.Owner) - .WithMany(p => p.Cars) - .HasForeignKey(d => d.OwnerId) - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("FK_Cars_Owners"); - }); - - modelBuilder.Entity(entity => - { - entity.Property(e => e.FirstName).IsUnicode(false); - - entity.Property(e => e.FullName).IsUnicode(false); - - entity.Property(e => e.LastName).IsUnicode(false); - }); - } - } -} +using HappyCode.NetCoreBoilerplate.Core.Models; +using Microsoft.EntityFrameworkCore; + +namespace HappyCode.NetCoreBoilerplate.Core +{ + public partial class CarsContext : DbContext + { + public CarsContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet Cars { get; set; } + public virtual DbSet Owners { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity(entity => + { + entity.Property(e => e.Model).IsUnicode(false); + + entity.Property(e => e.Plate).IsUnicode(false); + + entity.HasOne(d => d.Owner) + .WithMany(p => p.Cars) + .HasForeignKey(d => d.OwnerId) + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_Cars_Owners"); + }); + + modelBuilder.Entity(entity => + { + entity.Property(e => e.FirstName).IsUnicode(false); + + entity.Property(e => e.FullName).IsUnicode(false); + + entity.Property(e => e.LastName).IsUnicode(false); + }); + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/CarDto.cs b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/CarDto.cs index 0658e2a64..3362178e1 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/CarDto.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/CarDto.cs @@ -1,9 +1,9 @@ -namespace HappyCode.NetCoreBoilerplate.Core.Dtos -{ - public class CarDto - { - public int Id { get; set; } - public string Plate { get; set; } - public string Model { get; set; } - } -} +namespace HappyCode.NetCoreBoilerplate.Core.Dtos +{ + public class CarDto + { + public int Id { get; set; } + public string Plate { get; set; } + public string Model { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/DepartmentDto.cs b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/DepartmentDto.cs index 29f6660f1..77a41c1be 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/DepartmentDto.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/DepartmentDto.cs @@ -1,8 +1,8 @@ -namespace HappyCode.NetCoreBoilerplate.Core.Dtos -{ - public class DepartmentDto - { - public string Id { get; set; } - public string Name { get; set; } - } -} +namespace HappyCode.NetCoreBoilerplate.Core.Dtos +{ + public class DepartmentDto + { + public string Id { get; set; } + public string Name { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDetailsDto.cs b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDetailsDto.cs index 2a2a2a4ce..e361b1912 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDetailsDto.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDetailsDto.cs @@ -1,12 +1,12 @@ -namespace HappyCode.NetCoreBoilerplate.Core.Dtos -{ - public class EmployeeDetailsDto - { - public int Id { get; set; } - public DateTime BirthDate { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public string Gender { get; set; } - public DepartmentDto Department { get; set; } - } -} +namespace HappyCode.NetCoreBoilerplate.Core.Dtos +{ + public class EmployeeDetailsDto + { + public int Id { get; set; } + public DateTime BirthDate { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Gender { get; set; } + public DepartmentDto Department { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDto.cs b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDto.cs index c40affb0c..903dd3ef6 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDto.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeeDto.cs @@ -1,11 +1,11 @@ -namespace HappyCode.NetCoreBoilerplate.Core.Dtos -{ - public class EmployeeDto - { - public int Id { get; set; } - public DateTime BirthDate { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public string Gender { get; set; } - } -} +namespace HappyCode.NetCoreBoilerplate.Core.Dtos +{ + public class EmployeeDto + { + public int Id { get; set; } + public DateTime BirthDate { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Gender { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePostDto.cs b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePostDto.cs index d34dc7beb..5196c184a 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePostDto.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePostDto.cs @@ -1,17 +1,17 @@ -using System.ComponentModel.DataAnnotations; - -namespace HappyCode.NetCoreBoilerplate.Core.Dtos -{ - public class EmployeePostDto - { - [Required] - public DateTime? BirthDate { get; set; } - [Required] - public string FirstName { get; set; } - [Required] - public string LastName { get; set; } - [Required] - [RegularExpression("^[MF]$")] - public string Gender { get; set; } - } -} +using System.ComponentModel.DataAnnotations; + +namespace HappyCode.NetCoreBoilerplate.Core.Dtos +{ + public class EmployeePostDto + { + [Required] + public DateTime? BirthDate { get; set; } + [Required] + public string FirstName { get; set; } + [Required] + public string LastName { get; set; } + [Required] + [RegularExpression("^[MF]$")] + public string Gender { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePutDto.cs b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePutDto.cs index 8ba4673af..d585aa8a6 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePutDto.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Dtos/EmployeePutDto.cs @@ -1,10 +1,10 @@ -using System.ComponentModel.DataAnnotations; - -namespace HappyCode.NetCoreBoilerplate.Core.Dtos -{ - public class EmployeePutDto - { - [Required] - public string LastName { get; set; } - } -} +using System.ComponentModel.DataAnnotations; + +namespace HappyCode.NetCoreBoilerplate.Core.Dtos +{ + public class EmployeePutDto + { + [Required] + public string LastName { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/EmployeesContext.cs b/src/HappyCode.NetCoreBoilerplate.Core/EmployeesContext.cs index bc50a5a1d..af3522fb0 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/EmployeesContext.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/EmployeesContext.cs @@ -1,59 +1,59 @@ -using HappyCode.NetCoreBoilerplate.Core.Models; -using Microsoft.EntityFrameworkCore; - -namespace HappyCode.NetCoreBoilerplate.Core -{ - public partial class EmployeesContext : DbContext - { - public EmployeesContext(DbContextOptions options) - : base(options) - { - } - - public virtual DbSet Departments { get; set; } - public virtual DbSet Employees { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.DeptName) - .HasDatabaseName("dept_name") - .IsUnique(); - - entity.HasIndex(e => e.MangerNo) - .HasDatabaseName("manger_no"); - - entity.Property(e => e.DeptNo).ValueGeneratedNever(); - - entity.Property(e => e.DeptName).IsUnicode(false); - - entity.HasOne(d => d.Manger) - .WithMany(p => p.LeadingDepartments) - .HasForeignKey(d => d.MangerNo) - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("departments_ibfk_1"); - }); - - modelBuilder.Entity(entity => - { - entity.HasIndex(e => e.DeptNo) - .HasDatabaseName("dept_no"); - - entity.Property(e => e.EmpNo).ValueGeneratedOnAdd(); - - entity.Property(e => e.FirstName).IsUnicode(false); - - entity.Property(e => e.LastName).IsUnicode(false); - - entity.HasOne(d => d.Department) - .WithMany(p => p.Employees) - .HasForeignKey(d => d.DeptNo) - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("employees_ibfk_1"); - }); - } - } -} +using HappyCode.NetCoreBoilerplate.Core.Models; +using Microsoft.EntityFrameworkCore; + +namespace HappyCode.NetCoreBoilerplate.Core +{ + public partial class EmployeesContext : DbContext + { + public EmployeesContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet Departments { get; set; } + public virtual DbSet Employees { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.DeptName) + .HasDatabaseName("dept_name") + .IsUnique(); + + entity.HasIndex(e => e.MangerNo) + .HasDatabaseName("manger_no"); + + entity.Property(e => e.DeptNo).ValueGeneratedNever(); + + entity.Property(e => e.DeptName).IsUnicode(false); + + entity.HasOne(d => d.Manger) + .WithMany(p => p.LeadingDepartments) + .HasForeignKey(d => d.MangerNo) + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("departments_ibfk_1"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.DeptNo) + .HasDatabaseName("dept_no"); + + entity.Property(e => e.EmpNo).ValueGeneratedOnAdd(); + + entity.Property(e => e.FirstName).IsUnicode(false); + + entity.Property(e => e.LastName).IsUnicode(false); + + entity.HasOne(d => d.Department) + .WithMany(p => p.Employees) + .HasForeignKey(d => d.DeptNo) + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("employees_ibfk_1"); + }); + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Extensions/EmployeeExtensions.cs b/src/HappyCode.NetCoreBoilerplate.Core/Extensions/EmployeeExtensions.cs index c1604a535..cd3453117 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Extensions/EmployeeExtensions.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Extensions/EmployeeExtensions.cs @@ -1,20 +1,20 @@ -using HappyCode.NetCoreBoilerplate.Core.Dtos; -using HappyCode.NetCoreBoilerplate.Core.Models; - -namespace HappyCode.NetCoreBoilerplate.Core.Extensions -{ - internal static class EmployeeExtensions - { - public static EmployeeDto MapToDto(this Employee source) - { - return new EmployeeDto - { - Id = source.EmpNo, - FirstName = source.FirstName, - LastName = source.LastName, - BirthDate = source.BirthDate, - Gender = source.Gender, - }; - } - } -} +using HappyCode.NetCoreBoilerplate.Core.Dtos; +using HappyCode.NetCoreBoilerplate.Core.Models; + +namespace HappyCode.NetCoreBoilerplate.Core.Extensions +{ + internal static class EmployeeExtensions + { + public static EmployeeDto MapToDto(this Employee source) + { + return new EmployeeDto + { + Id = source.EmpNo, + FirstName = source.FirstName, + LastName = source.LastName, + BirthDate = source.BirthDate, + Gender = source.Gender, + }; + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/FeatureFlags.cs b/src/HappyCode.NetCoreBoilerplate.Core/FeatureFlags.cs index 75781fd4d..066779af2 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/FeatureFlags.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/FeatureFlags.cs @@ -1,8 +1,8 @@ -namespace HappyCode.NetCoreBoilerplate.Core -{ - public static class FeatureFlags - { - public const string ApiKey = nameof(ApiKey); - public const string Santa = nameof(Santa); - } -} +namespace HappyCode.NetCoreBoilerplate.Core +{ + public static class FeatureFlags + { + public const string ApiKey = nameof(ApiKey); + public const string Santa = nameof(Santa); + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/InternalsVisibleTo.cs b/src/HappyCode.NetCoreBoilerplate.Core/InternalsVisibleTo.cs index 5a817d37d..067dc3ff6 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/InternalsVisibleTo.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/InternalsVisibleTo.cs @@ -1,2 +1,2 @@ -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("HappyCode.NetCoreBoilerplate.Core.UnitTests")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("HappyCode.NetCoreBoilerplate.Core.UnitTests")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Models/Car.cs b/src/HappyCode.NetCoreBoilerplate.Core/Models/Car.cs index 3f5af899e..c942369fd 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Models/Car.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Models/Car.cs @@ -1,24 +1,24 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace HappyCode.NetCoreBoilerplate.Core.Models -{ - public partial class Car - { - public int Id { get; set; } - - [Required] - [StringLength(50)] - public string Plate { get; set; } - - [StringLength(50)] - public string Model { get; set; } - - public int? OwnerId { get; set; } - - - [ForeignKey("OwnerId")] - [InverseProperty("Cars")] - public virtual Owner Owner { get; set; } - } -} +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HappyCode.NetCoreBoilerplate.Core.Models +{ + public partial class Car + { + public int Id { get; set; } + + [Required] + [StringLength(50)] + public string Plate { get; set; } + + [StringLength(50)] + public string Model { get; set; } + + public int? OwnerId { get; set; } + + + [ForeignKey("OwnerId")] + [InverseProperty("Cars")] + public virtual Owner Owner { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Models/Department.cs b/src/HappyCode.NetCoreBoilerplate.Core/Models/Department.cs index 065ea54d1..fa28f57bf 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Models/Department.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Models/Department.cs @@ -1,29 +1,29 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace HappyCode.NetCoreBoilerplate.Core.Models -{ - [Table("departments", Schema = "employees")] - public class Department - { - [Key] - [Column("dept_no", TypeName = "char(4)")] - public string DeptNo { get; set; } - - [Required] - [Column("dept_name")] - [StringLength(40)] - public string DeptName { get; set; } - - [Column("manger_no", TypeName = "int(11)")] - public int MangerNo { get; set; } - - - [ForeignKey("MangerNo")] - [InverseProperty("LeadingDepartments")] - public virtual Employee Manger { get; set; } - - [InverseProperty("Department")] - public virtual ICollection Employees { get; set; } = new HashSet(); - } -} +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HappyCode.NetCoreBoilerplate.Core.Models +{ + [Table("departments", Schema = "employees")] + public class Department + { + [Key] + [Column("dept_no", TypeName = "char(4)")] + public string DeptNo { get; set; } + + [Required] + [Column("dept_name")] + [StringLength(40)] + public string DeptName { get; set; } + + [Column("manger_no", TypeName = "int(11)")] + public int MangerNo { get; set; } + + + [ForeignKey("MangerNo")] + [InverseProperty("LeadingDepartments")] + public virtual Employee Manger { get; set; } + + [InverseProperty("Department")] + public virtual ICollection Employees { get; set; } = new HashSet(); + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Models/Employee.cs b/src/HappyCode.NetCoreBoilerplate.Core/Models/Employee.cs index 4e6d364f0..ff20cb844 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Models/Employee.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Models/Employee.cs @@ -1,41 +1,41 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace HappyCode.NetCoreBoilerplate.Core.Models -{ - [Table("employees", Schema = "employees")] - public class Employee - { - [Key] - [Column("emp_no", TypeName = "int(11)")] - public int EmpNo { get; set; } - - [Column("birth_date", TypeName = "date")] - public DateTime BirthDate { get; set; } - - [Required] - [Column("first_name")] - [StringLength(14)] - public string FirstName { get; set; } - - [Required] - [Column("last_name")] - [StringLength(16)] - public string LastName { get; set; } - - [Required] - [Column("gender", TypeName = "enum('M','F')")] - public string Gender { get; set; } - - [Column("dept_no", TypeName = "char(4)")] - public string DeptNo { get; set; } - - - [ForeignKey("DeptNo")] - [InverseProperty("Employees")] - public virtual Department Department { get; set; } - - [InverseProperty("Manger")] - public virtual ICollection LeadingDepartments { get; set; } = new HashSet(); - } -} +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HappyCode.NetCoreBoilerplate.Core.Models +{ + [Table("employees", Schema = "employees")] + public class Employee + { + [Key] + [Column("emp_no", TypeName = "int(11)")] + public int EmpNo { get; set; } + + [Column("birth_date", TypeName = "date")] + public DateTime BirthDate { get; set; } + + [Required] + [Column("first_name")] + [StringLength(14)] + public string FirstName { get; set; } + + [Required] + [Column("last_name")] + [StringLength(16)] + public string LastName { get; set; } + + [Required] + [Column("gender", TypeName = "enum('M','F')")] + public string Gender { get; set; } + + [Column("dept_no", TypeName = "char(4)")] + public string DeptNo { get; set; } + + + [ForeignKey("DeptNo")] + [InverseProperty("Employees")] + public virtual Department Department { get; set; } + + [InverseProperty("Manger")] + public virtual ICollection LeadingDepartments { get; set; } = new HashSet(); + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Models/Owner.cs b/src/HappyCode.NetCoreBoilerplate.Core/Models/Owner.cs index 5a717bb03..6d5bb3899 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Models/Owner.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Models/Owner.cs @@ -1,25 +1,25 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace HappyCode.NetCoreBoilerplate.Core.Models -{ - public partial class Owner - { - public int Id { get; set; } - - [Required] - [StringLength(50)] - public string FirstName { get; set; } - - [Required] - [StringLength(50)] - public string LastName { get; set; } - - [DatabaseGenerated(DatabaseGeneratedOption.Computed)] - public string FullName { get; set; } - - - [InverseProperty("Owner")] - public virtual ICollection Cars { get; set; } = new HashSet(); - } -} +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HappyCode.NetCoreBoilerplate.Core.Models +{ + public partial class Owner + { + public int Id { get; set; } + + [Required] + [StringLength(50)] + public string FirstName { get; set; } + + [Required] + [StringLength(50)] + public string LastName { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public string FullName { get; set; } + + + [InverseProperty("Owner")] + public virtual ICollection Cars { get; set; } = new HashSet(); + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Registrations/CoreRegistrations.cs b/src/HappyCode.NetCoreBoilerplate.Core/Registrations/CoreRegistrations.cs index b3f177e7a..8090327a4 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Registrations/CoreRegistrations.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Registrations/CoreRegistrations.cs @@ -1,17 +1,17 @@ -using HappyCode.NetCoreBoilerplate.Core.Repositories; -using HappyCode.NetCoreBoilerplate.Core.Services; -using Microsoft.Extensions.DependencyInjection; - -namespace HappyCode.NetCoreBoilerplate.Core.Registrations -{ - public static class CoreRegistrations - { - public static IServiceCollection AddCoreComponents(this IServiceCollection services) - { - services.AddTransient(); - services.AddScoped(); - - return services; - } - } -} +using HappyCode.NetCoreBoilerplate.Core.Repositories; +using HappyCode.NetCoreBoilerplate.Core.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace HappyCode.NetCoreBoilerplate.Core.Registrations +{ + public static class CoreRegistrations + { + public static IServiceCollection AddCoreComponents(this IServiceCollection services) + { + services.AddTransient(); + services.AddScoped(); + + return services; + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Repositories/EmployeeRepository.cs b/src/HappyCode.NetCoreBoilerplate.Core/Repositories/EmployeeRepository.cs index 29749b197..24fc4ec1d 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Repositories/EmployeeRepository.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Repositories/EmployeeRepository.cs @@ -1,134 +1,134 @@ -using System.Threading; -using System.Threading.Tasks; -using HappyCode.NetCoreBoilerplate.Core.Models; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using HappyCode.NetCoreBoilerplate.Core.Dtos; -using HappyCode.NetCoreBoilerplate.Core.Extensions; - -namespace HappyCode.NetCoreBoilerplate.Core.Repositories -{ - public interface IEmployeeRepository - { - Task> GetAllAsync(CancellationToken cancellationToken); - Task GetByIdAsync(int id, CancellationToken cancellationToken); - Task GetByIdWithDetailsAsync(object id, CancellationToken cancellationToken); - Task GetOldestAsync(CancellationToken cancellationToken); - Task DeleteByIdAsync(int id, CancellationToken cancellationToken); - Task InsertAsync(EmployeePostDto employeePostDto, CancellationToken cancellationToken); - Task UpdateAsync(int id, EmployeePutDto employeePutDto, CancellationToken cancellationToken); - } - - internal class EmployeeRepository : RepositoryBase, IEmployeeRepository - { - public EmployeeRepository(EmployeesContext dbContext) : base(dbContext) - { - - } - - public async Task> GetAllAsync(CancellationToken cancellationToken) - { - var employees = await DbContext.Employees - .AsNoTracking() - .ToListAsync(cancellationToken); - - return employees.Select(EmployeeExtensions.MapToDto).ToList(); - } - - public async Task GetByIdAsync(int id, CancellationToken cancellationToken) - { - var emp = await DbContext.Employees - .AsNoTracking() - .SingleOrDefaultAsync(x => x.EmpNo == id, cancellationToken); - if (emp == null) - { - return null; - } - - return emp.MapToDto(); - } - - public async Task GetByIdWithDetailsAsync(object id, CancellationToken cancellationToken) - { - var emp = await DbContext.Employees - .Include(x => x.Department) - .SingleOrDefaultAsync(x => x.EmpNo == (int)id, cancellationToken); - if (emp == null) - { - return null; - } - - return new EmployeeDetailsDto - { - Id = emp.EmpNo, - FirstName = emp.FirstName, - LastName = emp.LastName, - BirthDate = emp.BirthDate, - Gender = emp.Gender, - Department = new DepartmentDto - { - Id = emp.Department.DeptNo, - Name = emp.Department.DeptName, - } - }; - } - - public async Task GetOldestAsync(CancellationToken cancellationToken) - { - var emp = await DbContext.Employees - .OrderBy(x => x.BirthDate) - .FirstOrDefaultAsync(cancellationToken); - if (emp == null) - { - return null; - } - - return emp.MapToDto(); - } - - public async Task InsertAsync(EmployeePostDto employeePostDto, CancellationToken cancellationToken) - { - var employee = new Employee - { - FirstName = employeePostDto.FirstName, - LastName = employeePostDto.LastName, - BirthDate = employeePostDto.BirthDate.Value, - Gender = employeePostDto.Gender, - }; - - await DbContext.Employees.AddAsync(employee, cancellationToken); - await DbContext.SaveChangesAsync(cancellationToken); - - return employee.MapToDto(); - } - - public async Task UpdateAsync(int id, EmployeePutDto employeePutDto, CancellationToken cancellationToken) - { - var emp = await DbContext.Employees - .SingleOrDefaultAsync(x => x.EmpNo == id, cancellationToken); - if (emp is null) - { - return null; - } - - emp.LastName = employeePutDto.LastName; - - await DbContext.SaveChangesAsync(cancellationToken); - - return emp.MapToDto(); - } - - public async Task DeleteByIdAsync(int id, CancellationToken cancellationToken) - { - var emp = await DbContext.Employees - .SingleOrDefaultAsync(x => x.EmpNo == id, cancellationToken); - if (emp == null) - { - return false; - } - - DbContext.Employees.Remove(emp); - return await DbContext.SaveChangesAsync(cancellationToken) > 0; - } - } -} +using System.Threading; +using System.Threading.Tasks; +using HappyCode.NetCoreBoilerplate.Core.Models; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using HappyCode.NetCoreBoilerplate.Core.Dtos; +using HappyCode.NetCoreBoilerplate.Core.Extensions; + +namespace HappyCode.NetCoreBoilerplate.Core.Repositories +{ + public interface IEmployeeRepository + { + Task> GetAllAsync(CancellationToken cancellationToken); + Task GetByIdAsync(int id, CancellationToken cancellationToken); + Task GetByIdWithDetailsAsync(object id, CancellationToken cancellationToken); + Task GetOldestAsync(CancellationToken cancellationToken); + Task DeleteByIdAsync(int id, CancellationToken cancellationToken); + Task InsertAsync(EmployeePostDto employeePostDto, CancellationToken cancellationToken); + Task UpdateAsync(int id, EmployeePutDto employeePutDto, CancellationToken cancellationToken); + } + + internal class EmployeeRepository : RepositoryBase, IEmployeeRepository + { + public EmployeeRepository(EmployeesContext dbContext) : base(dbContext) + { + + } + + public async Task> GetAllAsync(CancellationToken cancellationToken) + { + var employees = await DbContext.Employees + .AsNoTracking() + .ToListAsync(cancellationToken); + + return employees.Select(EmployeeExtensions.MapToDto).ToList(); + } + + public async Task GetByIdAsync(int id, CancellationToken cancellationToken) + { + var emp = await DbContext.Employees + .AsNoTracking() + .SingleOrDefaultAsync(x => x.EmpNo == id, cancellationToken); + if (emp == null) + { + return null; + } + + return emp.MapToDto(); + } + + public async Task GetByIdWithDetailsAsync(object id, CancellationToken cancellationToken) + { + var emp = await DbContext.Employees + .Include(x => x.Department) + .SingleOrDefaultAsync(x => x.EmpNo == (int)id, cancellationToken); + if (emp == null) + { + return null; + } + + return new EmployeeDetailsDto + { + Id = emp.EmpNo, + FirstName = emp.FirstName, + LastName = emp.LastName, + BirthDate = emp.BirthDate, + Gender = emp.Gender, + Department = new DepartmentDto + { + Id = emp.Department.DeptNo, + Name = emp.Department.DeptName, + } + }; + } + + public async Task GetOldestAsync(CancellationToken cancellationToken) + { + var emp = await DbContext.Employees + .OrderBy(x => x.BirthDate) + .FirstOrDefaultAsync(cancellationToken); + if (emp == null) + { + return null; + } + + return emp.MapToDto(); + } + + public async Task InsertAsync(EmployeePostDto employeePostDto, CancellationToken cancellationToken) + { + var employee = new Employee + { + FirstName = employeePostDto.FirstName, + LastName = employeePostDto.LastName, + BirthDate = employeePostDto.BirthDate.Value, + Gender = employeePostDto.Gender, + }; + + await DbContext.Employees.AddAsync(employee, cancellationToken); + await DbContext.SaveChangesAsync(cancellationToken); + + return employee.MapToDto(); + } + + public async Task UpdateAsync(int id, EmployeePutDto employeePutDto, CancellationToken cancellationToken) + { + var emp = await DbContext.Employees + .SingleOrDefaultAsync(x => x.EmpNo == id, cancellationToken); + if (emp is null) + { + return null; + } + + emp.LastName = employeePutDto.LastName; + + await DbContext.SaveChangesAsync(cancellationToken); + + return emp.MapToDto(); + } + + public async Task DeleteByIdAsync(int id, CancellationToken cancellationToken) + { + var emp = await DbContext.Employees + .SingleOrDefaultAsync(x => x.EmpNo == id, cancellationToken); + if (emp == null) + { + return false; + } + + DbContext.Employees.Remove(emp); + return await DbContext.SaveChangesAsync(cancellationToken) > 0; + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Repositories/RepositoryBase.cs b/src/HappyCode.NetCoreBoilerplate.Core/Repositories/RepositoryBase.cs index 79f0eaa59..4725ec2d6 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Repositories/RepositoryBase.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Repositories/RepositoryBase.cs @@ -1,13 +1,13 @@ -namespace HappyCode.NetCoreBoilerplate.Core.Repositories -{ - internal abstract class RepositoryBase - where TEntity : class - { - protected EmployeesContext DbContext { get; } - - protected RepositoryBase(EmployeesContext dbContext) - { - DbContext = dbContext; - } - } -} +namespace HappyCode.NetCoreBoilerplate.Core.Repositories +{ + internal abstract class RepositoryBase + where TEntity : class + { + protected EmployeesContext DbContext { get; } + + protected RepositoryBase(EmployeesContext dbContext) + { + DbContext = dbContext; + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Services/CarService.cs b/src/HappyCode.NetCoreBoilerplate.Core/Services/CarService.cs index 3e4a94a72..d0800802c 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Services/CarService.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Services/CarService.cs @@ -1,38 +1,38 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using HappyCode.NetCoreBoilerplate.Core.Dtos; -using Microsoft.EntityFrameworkCore; - -namespace HappyCode.NetCoreBoilerplate.Core.Services -{ - public interface ICarService - { - Task> GetAllSortedByPlateAsync(CancellationToken cancellationToken); - } - - internal class CarService : ICarService - { - private readonly CarsContext _dbContext; - - public CarService(CarsContext dbContext) - { - _dbContext = dbContext; - } - - public async Task> GetAllSortedByPlateAsync(CancellationToken cancellationToken) - { - var cars = await _dbContext.Cars - .AsNoTracking() - .OrderBy(x => x.Plate) - .ToListAsync(cancellationToken); - - return cars.Select(x => new CarDto - { - Id = x.Id, - Plate = x.Plate, - Model = x.Model, - }); - } - } -} +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using HappyCode.NetCoreBoilerplate.Core.Dtos; +using Microsoft.EntityFrameworkCore; + +namespace HappyCode.NetCoreBoilerplate.Core.Services +{ + public interface ICarService + { + Task> GetAllSortedByPlateAsync(CancellationToken cancellationToken); + } + + internal class CarService : ICarService + { + private readonly CarsContext _dbContext; + + public CarService(CarsContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetAllSortedByPlateAsync(CancellationToken cancellationToken) + { + var cars = await _dbContext.Cars + .AsNoTracking() + .OrderBy(x => x.Plate) + .ToListAsync(cancellationToken); + + return cars.Select(x => new CarDto + { + Id = x.Id, + Plate = x.Plate, + Model = x.Model, + }); + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Settings/ApiKeySettings.cs b/src/HappyCode.NetCoreBoilerplate.Core/Settings/ApiKeySettings.cs index e3bbd19b2..04baea57b 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Settings/ApiKeySettings.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Settings/ApiKeySettings.cs @@ -1,7 +1,7 @@ -namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations -{ - public class ApiKeySettings - { - public string SecretKey { get; set; } - } -} +namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations +{ + public class ApiKeySettings + { + public string SecretKey { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Core/Settings/PingWebsiteSettings.cs b/src/HappyCode.NetCoreBoilerplate.Core/Settings/PingWebsiteSettings.cs index cf0df839c..4d9306470 100644 --- a/src/HappyCode.NetCoreBoilerplate.Core/Settings/PingWebsiteSettings.cs +++ b/src/HappyCode.NetCoreBoilerplate.Core/Settings/PingWebsiteSettings.cs @@ -1,8 +1,8 @@ -namespace HappyCode.NetCoreBoilerplate.Core.Settings -{ - public class PingWebsiteSettings - { - public Uri Url { get; set; } - public int TimeIntervalInMinutes { get; set; } - } -} +namespace HappyCode.NetCoreBoilerplate.Core.Settings +{ + public class PingWebsiteSettings + { + public Uri Url { get; set; } + public int TimeIntervalInMinutes { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Db/Options/UpgradeOptions.cs b/src/HappyCode.NetCoreBoilerplate.Db/Options/UpgradeOptions.cs index 2be626671..8540b166b 100644 --- a/src/HappyCode.NetCoreBoilerplate.Db/Options/UpgradeOptions.cs +++ b/src/HappyCode.NetCoreBoilerplate.Db/Options/UpgradeOptions.cs @@ -1,8 +1,8 @@ -namespace HappyCode.NetCoreBoilerplate.Db.Options -{ - public class UpgradeOptions - { - public int CommandExecutionTimeoutSeconds { get; set; } - public string ScriptsAndCodePattern { get; set; } - } -} +namespace HappyCode.NetCoreBoilerplate.Db.Options +{ + public class UpgradeOptions + { + public int CommandExecutionTimeoutSeconds { get; set; } + public string ScriptsAndCodePattern { get; set; } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Db/Program.cs b/src/HappyCode.NetCoreBoilerplate.Db/Program.cs index cb1ae2208..5a99d03e7 100644 --- a/src/HappyCode.NetCoreBoilerplate.Db/Program.cs +++ b/src/HappyCode.NetCoreBoilerplate.Db/Program.cs @@ -1,51 +1,51 @@ -using System; -using System.IO; -using System.Reflection; -using System.Text.RegularExpressions; -using DbUp; -using HappyCode.NetCoreBoilerplate.Db.Options; -using Microsoft.Extensions.Configuration; - -namespace HappyCode.NetCoreBoilerplate.Db -{ - class Program - { - public static int Main(string[] args) - { - var configuration = LoadAppConfiguration(); - var upgradeOptions = configuration.GetSection("UpgradeOptions").Get(); - var scriptsAndCodePatternPattern = new Regex(upgradeOptions.ScriptsAndCodePattern ?? ".*", RegexOptions.IgnoreCase | RegexOptions.Compiled); - - var upgrader = DeployChanges.To - .SqlDatabase(configuration.GetConnectionString("MsSqlDb")) - .WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(), (fileName) => scriptsAndCodePatternPattern.IsMatch(fileName)) - .WithExecutionTimeout(TimeSpan.FromSeconds(upgradeOptions.CommandExecutionTimeoutSeconds)) - .WithTransaction() - .LogToConsole() - .Build(); - - var result = upgrader.PerformUpgrade(); - - if (!result.Successful) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(result.Error); - return -1; - } - - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("Success!"); - return 0; - } - - private static IConfigurationRoot LoadAppConfiguration() - { - return new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true) - .AddEnvironmentVariables() - .Build(); - } - } -} +using System; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using DbUp; +using HappyCode.NetCoreBoilerplate.Db.Options; +using Microsoft.Extensions.Configuration; + +namespace HappyCode.NetCoreBoilerplate.Db +{ + class Program + { + public static int Main(string[] args) + { + var configuration = LoadAppConfiguration(); + var upgradeOptions = configuration.GetSection("UpgradeOptions").Get(); + var scriptsAndCodePatternPattern = new Regex(upgradeOptions.ScriptsAndCodePattern ?? ".*", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + var upgrader = DeployChanges.To + .SqlDatabase(configuration.GetConnectionString("MsSqlDb")) + .WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(), (fileName) => scriptsAndCodePatternPattern.IsMatch(fileName)) + .WithExecutionTimeout(TimeSpan.FromSeconds(upgradeOptions.CommandExecutionTimeoutSeconds)) + .WithTransaction() + .LogToConsole() + .Build(); + + var result = upgrader.PerformUpgrade(); + + if (!result.Successful) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(result.Error); + return -1; + } + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("Success!"); + return 0; + } + + private static IConfigurationRoot LoadAppConfiguration() + { + return new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true) + .AddEnvironmentVariables() + .Build(); + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Code/S002_ModifySomeRows.cs b/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Code/S002_ModifySomeRows.cs index c42ac4aec..52dd76402 100644 --- a/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Code/S002_ModifySomeRows.cs +++ b/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Code/S002_ModifySomeRows.cs @@ -1,20 +1,20 @@ -using System; -using System.Data; -using DbUp.Engine; - -namespace HappyCode.NetCoreBoilerplate.Db.Scripts.Code -{ - public class S002_ModifySomeRows : IScript - { - public string ProvideScript(Func dbCommandFactory) - { - // just an example - using var cmd = dbCommandFactory(); - - cmd.CommandText = "SELECT COUNT(*) FROM Cars"; - int count = (int)cmd.ExecuteScalar(); - - return @$"UPDATE Cars SET Plate = Plate + '+{count}' WHERE Id = 2"; - } - } -} +using System; +using System.Data; +using DbUp.Engine; + +namespace HappyCode.NetCoreBoilerplate.Db.Scripts.Code +{ + public class S002_ModifySomeRows : IScript + { + public string ProvideScript(Func dbCommandFactory) + { + // just an example + using var cmd = dbCommandFactory(); + + cmd.CommandText = "SELECT COUNT(*) FROM Cars"; + int count = (int)cmd.ExecuteScalar(); + + return @$"UPDATE Cars SET Plate = Plate + '+{count}' WHERE Id = 2"; + } + } +} diff --git a/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Sql/S001_AddCarTypesTable.sql b/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Sql/S001_AddCarTypesTable.sql index f1ac048e4..9057e412f 100644 --- a/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Sql/S001_AddCarTypesTable.sql +++ b/src/HappyCode.NetCoreBoilerplate.Db/Scripts/Sql/S001_AddCarTypesTable.sql @@ -1,20 +1,20 @@ -CREATE TABLE [dbo].[CarTypes]( - [Id] [int] IDENTITY(1,1) NOT NULL, - [Type] [varchar](20) NOT NULL, - CONSTRAINT [PK_CarTypes] PRIMARY KEY CLUSTERED -( - [Id] ASC -)) ON [PRIMARY] -GO - -INSERT INTO [CarTypes] ([Type]) -VALUES ('Hatchback'), ('Kombi'), ('Sedan') -GO - -GRANT ALL ON CarTypes TO [user] -GO - -ALTER TABLE [dbo].[Cars] - ADD [CarTypeId] INT NULL CONSTRAINT [FK_Cars_CarTypes] FOREIGN KEY([CarTypeId]) REFERENCES [dbo].[CarTypes] ([Id]) - ON UPDATE CASCADE -GO +CREATE TABLE [dbo].[CarTypes]( + [Id] [int] IDENTITY(1,1) NOT NULL, + [Type] [varchar](20) NOT NULL, + CONSTRAINT [PK_CarTypes] PRIMARY KEY CLUSTERED +( + [Id] ASC +)) ON [PRIMARY] +GO + +INSERT INTO [CarTypes] ([Type]) +VALUES ('Hatchback'), ('Kombi'), ('Sedan') +GO + +GRANT ALL ON CarTypes TO [user] +GO + +ALTER TABLE [dbo].[Cars] + ADD [CarTypeId] INT NULL CONSTRAINT [FK_Cars_CarTypes] FOREIGN KEY([CarTypeId]) REFERENCES [dbo].[CarTypes] ([Id]) + ON UPDATE CASCADE +GO diff --git a/src/HappyCode.NetCoreBoilerplate.Db/appsettings.json b/src/HappyCode.NetCoreBoilerplate.Db/appsettings.json index 0201559b1..8421203b0 100644 --- a/src/HappyCode.NetCoreBoilerplate.Db/appsettings.json +++ b/src/HappyCode.NetCoreBoilerplate.Db/appsettings.json @@ -1,16 +1,16 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug" - } - }, - - "ConnectionStrings": { - "MsSqlDb": "Data Source=localhost;Initial Catalog=cars;User ID=migrator;Password=simplePWD123!" - }, - - "UpgradeOptions": { - "CommandExecutionTimeoutSeconds": 30, - "ScriptsAndCodePattern": "^.*\\.(cs|sql)$" - } -} +{ + "Logging": { + "LogLevel": { + "Default": "Debug" + } + }, + + "ConnectionStrings": { + "MsSqlDb": "Data Source=localhost;Initial Catalog=cars;User ID=migrator;Password=simplePWD123!" + }, + + "UpgradeOptions": { + "CommandExecutionTimeoutSeconds": 30, + "ScriptsAndCodePattern": "^.*\\.(cs|sql)$" + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Extensions/HttpContentExtensions.cs b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Extensions/HttpContentExtensions.cs index f2ce8742a..feb72c012 100644 --- a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Extensions/HttpContentExtensions.cs +++ b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Extensions/HttpContentExtensions.cs @@ -1,32 +1,32 @@ -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace HappyCode.NetCoreBoilerplate.Api.IntegrationTests.Extensions -{ - internal static class HttpContentExtensions - { - public static async Task ReadAsJsonAsync(this HttpContent content) - { - string json = await content.ReadAsStringAsync(); - var value = JsonConvert.DeserializeObject(json); - return value; - } - - public static HttpContent ToStringContent(this object source) - { - string json = source.ToJson(); - return new StringContent(json, Encoding.UTF8, "application/json"); - } - - private static string ToJson(this object source) - { - return JsonConvert.SerializeObject(source, new JsonSerializerSettings - { - Formatting = Formatting.None, - NullValueHandling = NullValueHandling.Ignore, - }); - } - } -} +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace HappyCode.NetCoreBoilerplate.Api.IntegrationTests.Extensions +{ + internal static class HttpContentExtensions + { + public static async Task ReadAsJsonAsync(this HttpContent content) + { + string json = await content.ReadAsStringAsync(); + var value = JsonConvert.DeserializeObject(json); + return value; + } + + public static HttpContent ToStringContent(this object source) + { + string json = source.ToJson(); + return new StringContent(json, Encoding.UTF8, "application/json"); + } + + private static string ToJson(this object source) + { + return JsonConvert.SerializeObject(source, new JsonSerializerSettings + { + Formatting = Formatting.None, + NullValueHandling = NullValueHandling.Ignore, + }); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/CarsContextDataFeeder.cs b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/CarsContextDataFeeder.cs index 7ea9befb4..4bede8805 100644 --- a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/CarsContextDataFeeder.cs +++ b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/CarsContextDataFeeder.cs @@ -1,27 +1,27 @@ -using HappyCode.NetCoreBoilerplate.Core; -using HappyCode.NetCoreBoilerplate.Core.Models; - -namespace HappyCode.NetCoreBoilerplate.Api.IntegrationTests.Infrastructure.DataFeeders -{ - internal static class CarsContextDataFeeder - { - public static void Feed(CarsContext dbContext) - { - var cars = new[] - { - new Car - { - Plate = "DW 12345", - Model = "Toyota Avensis", - }, - new Car - { - Plate = "SB 98765", - Model = "Mercedes-Benz", - }, - }; - dbContext.Cars.AddRange(cars); - dbContext.SaveChanges(); - } - } -} +using HappyCode.NetCoreBoilerplate.Core; +using HappyCode.NetCoreBoilerplate.Core.Models; + +namespace HappyCode.NetCoreBoilerplate.Api.IntegrationTests.Infrastructure.DataFeeders +{ + internal static class CarsContextDataFeeder + { + public static void Feed(CarsContext dbContext) + { + var cars = new[] + { + new Car + { + Plate = "DW 12345", + Model = "Toyota Avensis", + }, + new Car + { + Plate = "SB 98765", + Model = "Mercedes-Benz", + }, + }; + dbContext.Cars.AddRange(cars); + dbContext.SaveChanges(); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/EmployeesContextDataFeeder.cs b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/EmployeesContextDataFeeder.cs index 21a65fd94..a21cb12ca 100644 --- a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/EmployeesContextDataFeeder.cs +++ b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Infrastructure/DataFeeders/EmployeesContextDataFeeder.cs @@ -1,51 +1,51 @@ -using System; -using HappyCode.NetCoreBoilerplate.Core; -using HappyCode.NetCoreBoilerplate.Core.Models; - -namespace HappyCode.NetCoreBoilerplate.Api.IntegrationTests.Infrastructure.DataFeeders -{ - internal static class EmployeesContextDataFeeder - { - public static void Feed(EmployeesContext dbContext) - { - var dept1 = new Department - { - DeptNo = "D1", - DeptName = "Test department", - }; - dbContext.Departments.Add(dept1); - - dbContext.Employees.Add(new Employee - { - EmpNo = 1, - FirstName = "Thomas", - LastName = "Anderson", - BirthDate = new DateTime(1962, 03, 11), - Gender = "M", - Department = dept1, - }); - - dbContext.Employees.Add(new Employee - { - EmpNo = 2, - FirstName = "Jonathan", - LastName = "Fountain", - BirthDate = new DateTime(1954, 07, 19), - Gender = "M", - Department = dept1, - }); - - dbContext.Employees.Add(new Employee - { - EmpNo = 99, - FirstName = "Person", - LastName = "ToDelete", - BirthDate = new DateTime(2019, 10, 13), - Gender = "M", - Department = dept1, - }); - - dbContext.SaveChanges(); - } - } -} +using System; +using HappyCode.NetCoreBoilerplate.Core; +using HappyCode.NetCoreBoilerplate.Core.Models; + +namespace HappyCode.NetCoreBoilerplate.Api.IntegrationTests.Infrastructure.DataFeeders +{ + internal static class EmployeesContextDataFeeder + { + public static void Feed(EmployeesContext dbContext) + { + var dept1 = new Department + { + DeptNo = "D1", + DeptName = "Test department", + }; + dbContext.Departments.Add(dept1); + + dbContext.Employees.Add(new Employee + { + EmpNo = 1, + FirstName = "Thomas", + LastName = "Anderson", + BirthDate = new DateTime(1962, 03, 11), + Gender = "M", + Department = dept1, + }); + + dbContext.Employees.Add(new Employee + { + EmpNo = 2, + FirstName = "Jonathan", + LastName = "Fountain", + BirthDate = new DateTime(1954, 07, 19), + Gender = "M", + Department = dept1, + }); + + dbContext.Employees.Add(new Employee + { + EmpNo = 99, + FirstName = "Person", + LastName = "ToDelete", + BirthDate = new DateTime(2019, 10, 13), + Gender = "M", + Department = dept1, + }); + + dbContext.SaveChanges(); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Properties/launchSettings.json b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Properties/launchSettings.json index 21680871a..d494b3001 100644 --- a/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Properties/launchSettings.json +++ b/test/HappyCode.NetCoreBoilerplate.Api.IntegrationTests/Properties/launchSettings.json @@ -1,19 +1,19 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56916/", - "sslPort": 44307 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:56916/", + "sslPort": 44307 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/ControllerTestsBase.cs b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/ControllerTestsBase.cs index 8d04b1cf9..4dd21b538 100644 --- a/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/ControllerTestsBase.cs +++ b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/ControllerTestsBase.cs @@ -1,30 +1,30 @@ -using HappyCode.NetCoreBoilerplate.Api.Controllers; -using Microsoft.AspNetCore.Http; -using Moq.AutoMock; - -namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Controllers -{ - public abstract class ControllerTestsBase - where T : ApiControllerBase - { - protected readonly T Controller; - protected readonly AutoMocker Mocker; - - protected ControllerTestsBase() - { - Mocker = new AutoMocker(); - - var httpResponseMock = Mocker.GetMock(); - httpResponseMock.Setup(mock => mock.Headers).Returns(new HeaderDictionary()); - - var httpRequestMock = Mocker.GetMock(); - - var httpContextMock = Mocker.GetMock(); - httpContextMock.Setup(mock => mock.Response).Returns(httpResponseMock.Object); - httpContextMock.Setup(mock => mock.Request).Returns(httpRequestMock.Object); - - Controller = Mocker.CreateInstance(); - Controller.ControllerContext.HttpContext = httpContextMock.Object; - } - } -} +using HappyCode.NetCoreBoilerplate.Api.Controllers; +using Microsoft.AspNetCore.Http; +using Moq.AutoMock; + +namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Controllers +{ + public abstract class ControllerTestsBase + where T : ApiControllerBase + { + protected readonly T Controller; + protected readonly AutoMocker Mocker; + + protected ControllerTestsBase() + { + Mocker = new AutoMocker(); + + var httpResponseMock = Mocker.GetMock(); + httpResponseMock.Setup(mock => mock.Headers).Returns(new HeaderDictionary()); + + var httpRequestMock = Mocker.GetMock(); + + var httpContextMock = Mocker.GetMock(); + httpContextMock.Setup(mock => mock.Response).Returns(httpResponseMock.Object); + httpContextMock.Setup(mock => mock.Request).Returns(httpRequestMock.Object); + + Controller = Mocker.CreateInstance(); + Controller.ControllerContext.HttpContext = httpContextMock.Object; + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/EmployeesControllerTests.cs b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/EmployeesControllerTests.cs index a7b8e8410..1a96863c4 100644 --- a/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/EmployeesControllerTests.cs +++ b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Controllers/EmployeesControllerTests.cs @@ -1,219 +1,219 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using AutoFixture.Xunit2; -using FluentAssertions; -using HappyCode.NetCoreBoilerplate.Api.Controllers; -using HappyCode.NetCoreBoilerplate.Core; -using HappyCode.NetCoreBoilerplate.Core.Dtos; -using HappyCode.NetCoreBoilerplate.Core.Repositories; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.FeatureManagement; -using Moq; -using Xunit; - -namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Controllers -{ - public class EmployeesControllerTests : ControllerTestsBase - { - private readonly Mock _employeeRepositoryMock; - - public EmployeesControllerTests() - { - _employeeRepositoryMock = Mocker.GetMock(); - } - - [Theory, AutoData] - public async Task GetAll_should_return_expected_results(List employees) - { - //given - _employeeRepositoryMock.Setup(x => x.GetAllAsync(It.IsAny())) - .ReturnsAsync(employees); - - //when - var result = await Controller.GetAllAsync(default) as OkObjectResult; - - //then - result.Should().NotBeNull(); - result.Value.Should().BeAssignableTo>() - .And.BeEquivalentTo(employees); - - _employeeRepositoryMock.Verify(x => x.GetAllAsync(It.IsAny()), Times.Once); - } - - [Fact] - public async Task Get_should_call_GetByIdAsync_onto_repository() - { - //given - const int empId = 11; - - //when - await Controller.GetAsync(11, default); - - //then - _employeeRepositoryMock.Verify(x => x.GetByIdAsync(empId, It.IsAny()), Times.Once); - } - - [Fact] - public async Task Get_should_return_NotFound_when_repository_return_null() - { - //given - _employeeRepositoryMock.Setup(x => x.GetByIdAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(() => null); - - //when - var result = await Controller.GetAsync(1, default) as StatusCodeResult; - - //then - result.Should().NotBeNull(); - result.StatusCode.Should().Be(StatusCodes.Status404NotFound); - } - - [Fact] - public async Task Get_should_return_Ok_with_expected_result_when_repository_return_object() - { - //given - const int empId = 22; - const string lastName = "Smith"; - - _employeeRepositoryMock.Setup(x => x.GetByIdAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new EmployeeDto - { - Id = empId, - LastName = lastName, - }); - - //when - var result = await Controller.GetAsync(1, default) as OkObjectResult; - - //then - result.Should().NotBeNull(); - result.StatusCode.Should().Be(StatusCodes.Status200OK); - result.Value.Should().BeAssignableTo(); - var emp = result.Value as EmployeeDto; - emp.Id.Should().Be(empId); - emp.LastName.Should() - .NotBeNullOrEmpty() - .And.Be(lastName); - } - - [Fact] - public async Task Delete_should_call_DeleteByIdAsync_onto_repository() - { - //given - const int empId = 11; - - //when - await Controller.DeleteAsync(11, default); - - //then - _employeeRepositoryMock.Verify(x => x.DeleteByIdAsync(empId, It.IsAny()), Times.Once); - } - - [Fact] - public async Task Delete_should_return_NotFound_when_repository_return_false() - { - //given - _employeeRepositoryMock.Setup(x => x.DeleteByIdAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(false); - - //when - var result = await Controller.DeleteAsync(1, default) as StatusCodeResult; - - //then - result.Should().NotBeNull(); - result.StatusCode.Should().Be(StatusCodes.Status404NotFound); - } - - [Fact] - public async Task Delete_should_return_NoContent_when_repository_return_true() - { - //given - _employeeRepositoryMock.Setup(x => x.DeleteByIdAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(true); - - //when - var result = await Controller.DeleteAsync(1, default) as StatusCodeResult; - - //then - result.Should().NotBeNull(); - result.StatusCode.Should().Be(StatusCodes.Status204NoContent); - } - - [Theory, AutoData] - public async Task Put_should_return_NotFound_when_repository_return_null(int empId, EmployeePutDto employeePutDto) - { - //given - _employeeRepositoryMock.Setup(x => x.UpdateAsync(empId, employeePutDto, It.IsAny())) - .ReturnsAsync(() => null) - .Verifiable(); - - //when - var result = await Controller.PutAsync(empId, employeePutDto, default) as StatusCodeResult; - - //then - result.StatusCode.Should().Be(StatusCodes.Status404NotFound); - - _employeeRepositoryMock.Verify(); - } - - [Theory, AutoData] - public async Task Put_should_return_Ok_with_result_when_update_finished_with_success(int empId, EmployeePutDto employeePutDto, EmployeeDto employee) - { - //given - _employeeRepositoryMock.Setup(x => x.UpdateAsync(empId, employeePutDto, It.IsAny())) - .ReturnsAsync(employee) - .Verifiable(); - - //when - var result = await Controller.PutAsync(empId, employeePutDto, default) as OkObjectResult; - - //then - result.Value.Should().BeAssignableTo() - .And.BeEquivalentTo(employee); - - _employeeRepositoryMock.Verify(); - } - - [Theory, AutoData] - public async Task Post_should_return_Created_with_result_and_header_when_insert_finished_with_success(EmployeePostDto employeePostDto, EmployeeDto employee) - { - //given - _employeeRepositoryMock.Setup(x => x.InsertAsync(employeePostDto, It.IsAny())) - .ReturnsAsync(employee) - .Verifiable(); - - //when - var result = await Controller.PostAsync(employeePostDto, default) as ObjectResult; - - //then - result.StatusCode.Should().Be(StatusCodes.Status201Created); - result.Value.Should().BeAssignableTo() - .And.BeEquivalentTo(employee); - - Controller.HttpContext.Response.Headers.TryGetValue("x-date-created", out _).Should().BeTrue(); - - _employeeRepositoryMock.Verify(); - } - - [Fact] - public async Task GetOldest_should_return_Santa_when_feature_enabled() - { - //given - var featureManagerMock = Mocker.GetMock(); - featureManagerMock.Setup(x => x.IsEnabledAsync(FeatureFlags.Santa)) - .ReturnsAsync(true) - .Verifiable(); - - //when - var result = await Controller.GetOldestAsync(default) as ObjectResult; - - //then - result.Should().NotBeNull(); - result.StatusCode.Should().Be(StatusCodes.Status200OK); - result.Value.Should().BeAssignableTo() - .Which.FirstName.Should().Be("Santa"); - } - } -} +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using AutoFixture.Xunit2; +using FluentAssertions; +using HappyCode.NetCoreBoilerplate.Api.Controllers; +using HappyCode.NetCoreBoilerplate.Core; +using HappyCode.NetCoreBoilerplate.Core.Dtos; +using HappyCode.NetCoreBoilerplate.Core.Repositories; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.FeatureManagement; +using Moq; +using Xunit; + +namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Controllers +{ + public class EmployeesControllerTests : ControllerTestsBase + { + private readonly Mock _employeeRepositoryMock; + + public EmployeesControllerTests() + { + _employeeRepositoryMock = Mocker.GetMock(); + } + + [Theory, AutoData] + public async Task GetAll_should_return_expected_results(List employees) + { + //given + _employeeRepositoryMock.Setup(x => x.GetAllAsync(It.IsAny())) + .ReturnsAsync(employees); + + //when + var result = await Controller.GetAllAsync(default) as OkObjectResult; + + //then + result.Should().NotBeNull(); + result.Value.Should().BeAssignableTo>() + .And.BeEquivalentTo(employees); + + _employeeRepositoryMock.Verify(x => x.GetAllAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Get_should_call_GetByIdAsync_onto_repository() + { + //given + const int empId = 11; + + //when + await Controller.GetAsync(11, default); + + //then + _employeeRepositoryMock.Verify(x => x.GetByIdAsync(empId, It.IsAny()), Times.Once); + } + + [Fact] + public async Task Get_should_return_NotFound_when_repository_return_null() + { + //given + _employeeRepositoryMock.Setup(x => x.GetByIdAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(() => null); + + //when + var result = await Controller.GetAsync(1, default) as StatusCodeResult; + + //then + result.Should().NotBeNull(); + result.StatusCode.Should().Be(StatusCodes.Status404NotFound); + } + + [Fact] + public async Task Get_should_return_Ok_with_expected_result_when_repository_return_object() + { + //given + const int empId = 22; + const string lastName = "Smith"; + + _employeeRepositoryMock.Setup(x => x.GetByIdAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new EmployeeDto + { + Id = empId, + LastName = lastName, + }); + + //when + var result = await Controller.GetAsync(1, default) as OkObjectResult; + + //then + result.Should().NotBeNull(); + result.StatusCode.Should().Be(StatusCodes.Status200OK); + result.Value.Should().BeAssignableTo(); + var emp = result.Value as EmployeeDto; + emp.Id.Should().Be(empId); + emp.LastName.Should() + .NotBeNullOrEmpty() + .And.Be(lastName); + } + + [Fact] + public async Task Delete_should_call_DeleteByIdAsync_onto_repository() + { + //given + const int empId = 11; + + //when + await Controller.DeleteAsync(11, default); + + //then + _employeeRepositoryMock.Verify(x => x.DeleteByIdAsync(empId, It.IsAny()), Times.Once); + } + + [Fact] + public async Task Delete_should_return_NotFound_when_repository_return_false() + { + //given + _employeeRepositoryMock.Setup(x => x.DeleteByIdAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(false); + + //when + var result = await Controller.DeleteAsync(1, default) as StatusCodeResult; + + //then + result.Should().NotBeNull(); + result.StatusCode.Should().Be(StatusCodes.Status404NotFound); + } + + [Fact] + public async Task Delete_should_return_NoContent_when_repository_return_true() + { + //given + _employeeRepositoryMock.Setup(x => x.DeleteByIdAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(true); + + //when + var result = await Controller.DeleteAsync(1, default) as StatusCodeResult; + + //then + result.Should().NotBeNull(); + result.StatusCode.Should().Be(StatusCodes.Status204NoContent); + } + + [Theory, AutoData] + public async Task Put_should_return_NotFound_when_repository_return_null(int empId, EmployeePutDto employeePutDto) + { + //given + _employeeRepositoryMock.Setup(x => x.UpdateAsync(empId, employeePutDto, It.IsAny())) + .ReturnsAsync(() => null) + .Verifiable(); + + //when + var result = await Controller.PutAsync(empId, employeePutDto, default) as StatusCodeResult; + + //then + result.StatusCode.Should().Be(StatusCodes.Status404NotFound); + + _employeeRepositoryMock.Verify(); + } + + [Theory, AutoData] + public async Task Put_should_return_Ok_with_result_when_update_finished_with_success(int empId, EmployeePutDto employeePutDto, EmployeeDto employee) + { + //given + _employeeRepositoryMock.Setup(x => x.UpdateAsync(empId, employeePutDto, It.IsAny())) + .ReturnsAsync(employee) + .Verifiable(); + + //when + var result = await Controller.PutAsync(empId, employeePutDto, default) as OkObjectResult; + + //then + result.Value.Should().BeAssignableTo() + .And.BeEquivalentTo(employee); + + _employeeRepositoryMock.Verify(); + } + + [Theory, AutoData] + public async Task Post_should_return_Created_with_result_and_header_when_insert_finished_with_success(EmployeePostDto employeePostDto, EmployeeDto employee) + { + //given + _employeeRepositoryMock.Setup(x => x.InsertAsync(employeePostDto, It.IsAny())) + .ReturnsAsync(employee) + .Verifiable(); + + //when + var result = await Controller.PostAsync(employeePostDto, default) as ObjectResult; + + //then + result.StatusCode.Should().Be(StatusCodes.Status201Created); + result.Value.Should().BeAssignableTo() + .And.BeEquivalentTo(employee); + + Controller.HttpContext.Response.Headers.TryGetValue("x-date-created", out _).Should().BeTrue(); + + _employeeRepositoryMock.Verify(); + } + + [Fact] + public async Task GetOldest_should_return_Santa_when_feature_enabled() + { + //given + var featureManagerMock = Mocker.GetMock(); + featureManagerMock.Setup(x => x.IsEnabledAsync(FeatureFlags.Santa)) + .ReturnsAsync(true) + .Verifiable(); + + //when + var result = await Controller.GetOldestAsync(default) as ObjectResult; + + //then + result.Should().NotBeNull(); + result.StatusCode.Should().Be(StatusCodes.Status200OK); + result.Value.Should().BeAssignableTo() + .Which.FirstName.Should().Be("Santa"); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/ValidateModelStateFilterTests.cs b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/ValidateModelStateFilterTests.cs index 07243a4de..e9d78aad6 100644 --- a/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/ValidateModelStateFilterTests.cs +++ b/test/HappyCode.NetCoreBoilerplate.Api.UnitTests/Infrastructure/Filters/ValidateModelStateFilterTests.cs @@ -1,68 +1,68 @@ -using System.Collections.Generic; -using FluentAssertions; -using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Abstractions; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Routing; -using Moq; -using Xunit; - -namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Infrastructure.Filters -{ - public class ValidateModelStateFilterTests - { - private readonly ValidateModelStateFilter _filter = new ValidateModelStateFilter(); - - [Fact] - public void When_ModelState_is_valid_Then_result_is_empty() - { - //when - var context = GetMockedContext(null, null); - _filter.OnActionExecuting(context); - - //then - context.Result.Should().BeNull(); - } - - [Fact] - public void When_ModelState_is_not_valid_Then_bad_request_returned() - { - //when - var context = GetMockedContext("some_field", "Some error message"); - _filter.OnActionExecuting(context); - - //then - context.Result.Should().NotBeNull(); - context.Result.Should().BeOfType() - .Which.Value.Should().BeOfType() - .Which.Messages.Should().ContainMatch("*error message*"); - } - - private ActionExecutingContext GetMockedContext(string key, string errorMessage) - { - var modelState = new ModelStateDictionary(); - - if (key != null) - { - modelState.AddModelError(key, errorMessage); - } - - var actionContext = new ActionContext( - Mock.Of(), - Mock.Of(), - Mock.Of(), - modelState - ); - - return new ActionExecutingContext( - actionContext, - new List(), - new Dictionary(), - Mock.Of() - ); - } - } -} +using System.Collections.Generic; +using FluentAssertions; +using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Routing; +using Moq; +using Xunit; + +namespace HappyCode.NetCoreBoilerplate.Api.UnitTests.Infrastructure.Filters +{ + public class ValidateModelStateFilterTests + { + private readonly ValidateModelStateFilter _filter = new ValidateModelStateFilter(); + + [Fact] + public void When_ModelState_is_valid_Then_result_is_empty() + { + //when + var context = GetMockedContext(null, null); + _filter.OnActionExecuting(context); + + //then + context.Result.Should().BeNull(); + } + + [Fact] + public void When_ModelState_is_not_valid_Then_bad_request_returned() + { + //when + var context = GetMockedContext("some_field", "Some error message"); + _filter.OnActionExecuting(context); + + //then + context.Result.Should().NotBeNull(); + context.Result.Should().BeOfType() + .Which.Value.Should().BeOfType() + .Which.Messages.Should().ContainMatch("*error message*"); + } + + private ActionExecutingContext GetMockedContext(string key, string errorMessage) + { + var modelState = new ModelStateDictionary(); + + if (key != null) + { + modelState.AddModelError(key, errorMessage); + } + + var actionContext = new ActionContext( + Mock.Of(), + Mock.Of(), + Mock.Of(), + modelState + ); + + return new ActionExecutingContext( + actionContext, + new List(), + new Dictionary(), + Mock.Of() + ); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/ApiArchitecturalTests.cs b/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/ApiArchitecturalTests.cs index 234a195b8..0d5c4d833 100644 --- a/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/ApiArchitecturalTests.cs +++ b/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/ApiArchitecturalTests.cs @@ -1,43 +1,43 @@ -using System; -using HappyCode.NetCoreBoilerplate.Api; -using HappyCode.NetCoreBoilerplate.Api.Controllers; -using NetArchTest.Rules; -using Xunit; - -namespace HappyCode.NetCoreBoilerplate.ArchitecturalTests -{ - public class ApiArchitecturalTests - { - private static readonly Types _apiTypes = Types.InAssembly(typeof(Startup).Assembly); - - [Fact] - public void Controllers_should_inherit_from_ApiControllerBase() - { - var result = _apiTypes - .That() - .ResideInNamespace("HappyCode.NetCoreBoilerplate.Api.Controllers") - .And() - .AreNotAbstract() - .Should() - .Inherit(typeof(ApiControllerBase)) - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - - [Fact] - public void Controllers_should_have_Controller_suffix() - { - var result = _apiTypes - .That() - .ResideInNamespace("HappyCode.NetCoreBoilerplate.Api.Controllers") - .And() - .AreNotAbstract() - .Should() - .HaveNameEndingWith("Controller") - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - } -} +using System; +using HappyCode.NetCoreBoilerplate.Api; +using HappyCode.NetCoreBoilerplate.Api.Controllers; +using NetArchTest.Rules; +using Xunit; + +namespace HappyCode.NetCoreBoilerplate.ArchitecturalTests +{ + public class ApiArchitecturalTests + { + private static readonly Types _apiTypes = Types.InAssembly(typeof(Startup).Assembly); + + [Fact] + public void Controllers_should_inherit_from_ApiControllerBase() + { + var result = _apiTypes + .That() + .ResideInNamespace("HappyCode.NetCoreBoilerplate.Api.Controllers") + .And() + .AreNotAbstract() + .Should() + .Inherit(typeof(ApiControllerBase)) + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + + [Fact] + public void Controllers_should_have_Controller_suffix() + { + var result = _apiTypes + .That() + .ResideInNamespace("HappyCode.NetCoreBoilerplate.Api.Controllers") + .And() + .AreNotAbstract() + .Should() + .HaveNameEndingWith("Controller") + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/CoreArchitecturalTests.cs b/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/CoreArchitecturalTests.cs index e1e06bcf3..7d6b026ad 100644 --- a/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/CoreArchitecturalTests.cs +++ b/test/HappyCode.NetCoreBoilerplate.ArchitecturalTests/CoreArchitecturalTests.cs @@ -1,93 +1,93 @@ -using System; -using HappyCode.NetCoreBoilerplate.Core; -using Microsoft.EntityFrameworkCore; -using NetArchTest.Rules; -using Xunit; - -namespace HappyCode.NetCoreBoilerplate.ArchitecturalTests -{ - public class CoreArchitecturalTests - { - private static readonly Types _coreTypes = Types.InAssembly(typeof(FeatureFlags).Assembly); - - [Fact] - public void Core_should_not_use_AspNetCore() - { - var result = _coreTypes - .ShouldNot() - .HaveDependencyOn("Microsoft.AspNetCore") - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - - [Fact] - public void Repositories_should_not_use_Services() - { - var result = _coreTypes - .That() - .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Repositories") - .ShouldNot() - .HaveDependencyOn("HappyCode.NetCoreBoilerplate.Core.Services") - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - - [Fact] - public void Repositories_should_have_Repository_suffix() - { - var result = _coreTypes - .That() - .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Repositories") - .And() - .AreNotAbstract() - .Should() - .HaveNameEndingWith("Repository") - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - - [Fact] - public void Services_should_have_Service_suffix() - { - var result = _coreTypes - .That() - .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Services") - .And() - .AreNotAbstract() - .Should() - .HaveNameEndingWith("Service") - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - - [Fact] - public void Dto_should_have_Dto_suffix() - { - var result = _coreTypes - .That() - .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Dtos") - .Should() - .HaveNameEndingWith("Dto") - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - - [Fact] - public void DbContext_should_have_Context_suffix() - { - var result = _coreTypes - .That() - .Inherit(typeof(DbContext)) - .Should() - .HaveNameEndingWith("Context") - .GetResult(); - - Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); - } - } -} +using System; +using HappyCode.NetCoreBoilerplate.Core; +using Microsoft.EntityFrameworkCore; +using NetArchTest.Rules; +using Xunit; + +namespace HappyCode.NetCoreBoilerplate.ArchitecturalTests +{ + public class CoreArchitecturalTests + { + private static readonly Types _coreTypes = Types.InAssembly(typeof(FeatureFlags).Assembly); + + [Fact] + public void Core_should_not_use_AspNetCore() + { + var result = _coreTypes + .ShouldNot() + .HaveDependencyOn("Microsoft.AspNetCore") + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + + [Fact] + public void Repositories_should_not_use_Services() + { + var result = _coreTypes + .That() + .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Repositories") + .ShouldNot() + .HaveDependencyOn("HappyCode.NetCoreBoilerplate.Core.Services") + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + + [Fact] + public void Repositories_should_have_Repository_suffix() + { + var result = _coreTypes + .That() + .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Repositories") + .And() + .AreNotAbstract() + .Should() + .HaveNameEndingWith("Repository") + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + + [Fact] + public void Services_should_have_Service_suffix() + { + var result = _coreTypes + .That() + .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Services") + .And() + .AreNotAbstract() + .Should() + .HaveNameEndingWith("Service") + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + + [Fact] + public void Dto_should_have_Dto_suffix() + { + var result = _coreTypes + .That() + .ResideInNamespace("HappyCode.NetCoreBoilerplate.Core.Dtos") + .Should() + .HaveNameEndingWith("Dto") + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + + [Fact] + public void DbContext_should_have_Context_suffix() + { + var result = _coreTypes + .That() + .Inherit(typeof(DbContext)) + .Should() + .HaveNameEndingWith("Context") + .GetResult(); + + Assert.True(result.IsSuccessful, $"Failing Types: {string.Join("; ", result.FailingTypeNames ?? Array.Empty())}"); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Extensions/HttpContentExtensions.cs b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Extensions/HttpContentExtensions.cs index 533ca78b4..1fa290b2f 100644 --- a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Extensions/HttpContentExtensions.cs +++ b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Extensions/HttpContentExtensions.cs @@ -1,26 +1,26 @@ -using System.Net.Http; -using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests.Extensions -{ - internal static class HttpContentExtensions - { - public static HttpContent ToStringContent(this object source) - { - string json = source.ToJson(); - return new StringContent(json, Encoding.UTF8, "application/json"); - } - - private static string ToJson(this object source) - { - return JsonConvert.SerializeObject(source, new JsonSerializerSettings - { - Formatting = Formatting.None, - NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver(), - }); - } - } -} +using System.Net.Http; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests.Extensions +{ + internal static class HttpContentExtensions + { + public static HttpContent ToStringContent(this object source) + { + string json = source.ToJson(); + return new StringContent(json, Encoding.UTF8, "application/json"); + } + + private static string ToJson(this object source) + { + return JsonConvert.SerializeObject(source, new JsonSerializerSettings + { + Formatting = Formatting.None, + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + }); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Infrastructure/DataFeeders/BooksDataFeeder.cs b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Infrastructure/DataFeeders/BooksDataFeeder.cs index 84b77731e..062b368bc 100644 --- a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Infrastructure/DataFeeders/BooksDataFeeder.cs +++ b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Infrastructure/DataFeeders/BooksDataFeeder.cs @@ -1,20 +1,20 @@ -using Dapper; -using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; -using System.Data; - -namespace HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests.Infrastructure.DataFeeders -{ - internal static class BooksDataFeeder - { - public static void Feed(IDbConnection db) - { - db.Execute(@$" -INSERT INTO Books ({nameof(BookDto.Id)}, {nameof(BookDto.Title)}) - VALUES(1, 'C# book');"); - - db.Execute(@$" -INSERT INTO Books ({nameof(BookDto.Id)}, {nameof(BookDto.Title)}) - VALUES(2, '.NET book');"); - } - } -} +using Dapper; +using HappyCode.NetCoreBoilerplate.BooksModule.Dtos; +using System.Data; + +namespace HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests.Infrastructure.DataFeeders +{ + internal static class BooksDataFeeder + { + public static void Feed(IDbConnection db) + { + db.Execute(@$" +INSERT INTO Books ({nameof(BookDto.Id)}, {nameof(BookDto.Title)}) + VALUES(1, 'C# book');"); + + db.Execute(@$" +INSERT INTO Books ({nameof(BookDto.Id)}, {nameof(BookDto.Title)}) + VALUES(2, '.NET book');"); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Properties/launchSettings.json b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Properties/launchSettings.json index 128699b79..a6b36faae 100644 --- a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Properties/launchSettings.json +++ b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Properties/launchSettings.json @@ -1,12 +1,12 @@ -{ - "profiles": { - "HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:57555;http://localhost:57556" - } - } +{ + "profiles": { + "HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:57555;http://localhost:57556" + } + } } \ No newline at end of file diff --git a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NoContent.verified.txt b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NoContent.verified.txt index 47eaf959c..8e14334db 100644 --- a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NoContent.verified.txt +++ b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NoContent.verified.txt @@ -1,8 +1,8 @@ -{ - Version: 1.1, - Status: 204 No Content, - RequestMessage: { - Method: DELETE, - Uri: http://localhost/api/books/1 - } +{ + Version: 1.1, + Status: 204 No Content, + RequestMessage: { + Method: DELETE, + Uri: http://localhost/api/books/1 + } } \ No newline at end of file diff --git a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NotFound_when_no_book.verified.txt b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NotFound_when_no_book.verified.txt index bac16b1b9..6b6e128d1 100644 --- a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NotFound_when_no_book.verified.txt +++ b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Delete_should_return_NotFound_when_no_book.verified.txt @@ -1,8 +1,8 @@ -{ - Version: 1.1, - Status: 404 Not Found, - RequestMessage: { - Method: DELETE, - Uri: http://localhost/api/books/2147483647 - } +{ + Version: 1.1, + Status: 404 Not Found, + RequestMessage: { + Method: DELETE, + Uri: http://localhost/api/books/2147483647 + } } \ No newline at end of file diff --git a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Get_should_return_NotFound_when_no_book.verified.txt b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Get_should_return_NotFound_when_no_book.verified.txt index cdcfb9a1a..9d3116b52 100644 --- a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Get_should_return_NotFound_when_no_book.verified.txt +++ b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Get_should_return_NotFound_when_no_book.verified.txt @@ -1,5 +1,5 @@ -{ - Version: 1.1, - Status: 404 Not Found, - RequestMessage: http://localhost/api/books/2147483647 +{ + Version: 1.1, + Status: 404 Not Found, + RequestMessage: http://localhost/api/books/2147483647 } \ No newline at end of file diff --git a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Post_should_return_NoContent.verified.txt b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Post_should_return_NoContent.verified.txt index 14af28286..c24dcf7eb 100644 --- a/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Post_should_return_NoContent.verified.txt +++ b/test/HappyCode.NetCoreBoilerplate.BooksModule.IntegrationTests/Verify/BooksTests.Post_should_return_NoContent.verified.txt @@ -1,17 +1,17 @@ -{ - Version: 1.1, - Status: 204 No Content, - RequestMessage: { - Method: POST, - Uri: http://localhost/api/books, - Content: { - Headers: { - Content-Length: 27, - Content-Type: application/json; charset=utf-8 - }, - Value: { - title: Some_test_title - } - } - } +{ + Version: 1.1, + Status: 204 No Content, + RequestMessage: { + Method: POST, + Uri: http://localhost/api/books, + Content: { + Headers: { + Content-Length: 27, + Content-Type: application/json; charset=utf-8 + }, + Value: { + title: Some_test_title + } + } + } } \ No newline at end of file diff --git a/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Extensions/EnumerableExtensions.cs b/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Extensions/EnumerableExtensions.cs index d2b00d47a..6e604d06b 100644 --- a/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Extensions/EnumerableExtensions.cs +++ b/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Extensions/EnumerableExtensions.cs @@ -1,23 +1,23 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.EntityFrameworkCore; -using MockQueryable.Moq; -using Moq; - -namespace HappyCode.NetCoreBoilerplate.Core.UnitTests.Extensions -{ - internal static class EnumerableExtensions - { - public static Mock> GetMockDbSet(this IEnumerable source) - where TEntity : class - { - return source.AsQueryable().BuildMockDbSet(); - } - - public static DbSet GetMockDbSetObject(this IEnumerable source) - where TEntity : class - { - return source.GetMockDbSet().Object; - } - } -} +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using MockQueryable.Moq; +using Moq; + +namespace HappyCode.NetCoreBoilerplate.Core.UnitTests.Extensions +{ + internal static class EnumerableExtensions + { + public static Mock> GetMockDbSet(this IEnumerable source) + where TEntity : class + { + return source.AsQueryable().BuildMockDbSet(); + } + + public static DbSet GetMockDbSetObject(this IEnumerable source) + where TEntity : class + { + return source.GetMockDbSet().Object; + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Repositories/EmployeeRepositoryTests.cs b/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Repositories/EmployeeRepositoryTests.cs index c1e078019..dbf533c7c 100644 --- a/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Repositories/EmployeeRepositoryTests.cs +++ b/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Repositories/EmployeeRepositoryTests.cs @@ -1,169 +1,169 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AutoFixture; -using AutoFixture.Xunit2; -using FluentAssertions; -using HappyCode.NetCoreBoilerplate.Core.Dtos; -using HappyCode.NetCoreBoilerplate.Core.Models; -using HappyCode.NetCoreBoilerplate.Core.Repositories; -using HappyCode.NetCoreBoilerplate.Core.UnitTests.Extensions; -using Microsoft.EntityFrameworkCore; -using Moq; -using Xunit; - -namespace HappyCode.NetCoreBoilerplate.Core.UnitTests.Repositories -{ - public class EmployeeRepositoryTests - { - private static readonly Fixture _fixture = new Fixture(); - - private readonly EmployeeRepository _repository; - private readonly Mock _dbContextMock; - - public EmployeeRepositoryTests() - { - _dbContextMock = new Mock(new DbContextOptionsBuilder().Options); - - _repository = new EmployeeRepository(_dbContextMock.Object); - } - - [Theory, AutoData] - public async Task GetByIdAsync_should_return_expected_employee(int empId) - { - //given - var employee = _fixture.Build() - .Without(x => x.LeadingDepartments) - .Without(x => x.Department) - .With(x => x.EmpNo, empId) - .Create(); - _dbContextMock.Setup(x => x.Employees).Returns(new[] { employee }.GetMockDbSetObject()); - - //when - var emp = await _repository.GetByIdAsync(empId, default); - - //then - emp.Id.Should().Be(empId); - } - - [Fact] - public async Task GetOldestAsync_should_return_expected_employee() - { - //given - var employees = _fixture.Build() - .Without(x => x.LeadingDepartments) - .Without(x => x.Department) - .CreateMany(20); - - _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); - - //when - var emp = await _repository.GetOldestAsync(default); - - //then - var theOldest = employees.OrderBy(x => x.BirthDate).First(); - - emp.Id.Should().Be(theOldest.EmpNo); - emp.LastName.Should() - .NotBeNullOrEmpty() - .And.Be(theOldest.LastName); - } - - [Fact] - public async Task DeleteByIdAsync_should_return_false_when_employee_not_found() - { - //given - _dbContextMock.Setup(x => x.Employees).Returns(Enumerable.Empty().GetMockDbSetObject); - - //when - var result = await _repository.DeleteByIdAsync(99, default); - - //then - result.Should().Be(false); - - _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never); - } - - [Theory, AutoData] - public async Task DeleteByIdAsync_should_return_true_and_save_when_employee_found(int empId) - { - //given - var employees = _fixture.Build() - .Without(x => x.LeadingDepartments) - .Without(x => x.Department) - .With(x => x.EmpNo, empId) - .CreateMany(1); - - _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); - - _dbContextMock.Setup(x => x.SaveChangesAsync(It.IsAny())) - .ReturnsAsync(1); - - //when - var result = await _repository.DeleteByIdAsync(empId, default); - - //then - result.Should().Be(true); - - _dbContextMock.Verify(x => x.Employees.Remove(It.Is(y => y.EmpNo == empId)), Times.Once); - _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); - } - - [Theory, AutoData] - public async Task InsertAsync_should_add_and_save(EmployeePostDto employeePostDto) - { - //given - var employees = _fixture.Build() - .Without(x => x.LeadingDepartments) - .Without(x => x.Department) - .CreateMany(3); - - _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); - - //when - var result = await _repository.InsertAsync(employeePostDto, default); - - //then - result.Should().BeEquivalentTo(employeePostDto); - - _dbContextMock.Verify(x => x.Employees.AddAsync(It.Is(y => y.LastName == employeePostDto.LastName), It.IsAny()), Times.Once); - _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); - } - - [Theory, AutoData] - public async Task UpdateAsync_should_return_null_when_employee_not_found(int empId, EmployeePutDto employeePutDto) - { - //given - _dbContextMock.Setup(x => x.Employees).Returns(Enumerable.Empty().GetMockDbSetObject); - - //when - var result = await _repository.UpdateAsync(empId, employeePutDto, default); - - //then - result.Should().BeNull(); - - _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never); - } - - [Theory, AutoData] - public async Task UpdateAsync_should_update_the_entity_and_save_when_employee_found(int empId, EmployeePutDto employeePutDto) - { - //given - var employees = _fixture.Build() - .Without(x => x.LeadingDepartments) - .Without(x => x.Department) - .With(x => x.EmpNo, empId) - .CreateMany(1); - - _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); - - //when - var result = await _repository.UpdateAsync(empId, employeePutDto, default); - - //then - result.LastName.Should().Be(employeePutDto.LastName); - - _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); - } - } -} +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.Xunit2; +using FluentAssertions; +using HappyCode.NetCoreBoilerplate.Core.Dtos; +using HappyCode.NetCoreBoilerplate.Core.Models; +using HappyCode.NetCoreBoilerplate.Core.Repositories; +using HappyCode.NetCoreBoilerplate.Core.UnitTests.Extensions; +using Microsoft.EntityFrameworkCore; +using Moq; +using Xunit; + +namespace HappyCode.NetCoreBoilerplate.Core.UnitTests.Repositories +{ + public class EmployeeRepositoryTests + { + private static readonly Fixture _fixture = new Fixture(); + + private readonly EmployeeRepository _repository; + private readonly Mock _dbContextMock; + + public EmployeeRepositoryTests() + { + _dbContextMock = new Mock(new DbContextOptionsBuilder().Options); + + _repository = new EmployeeRepository(_dbContextMock.Object); + } + + [Theory, AutoData] + public async Task GetByIdAsync_should_return_expected_employee(int empId) + { + //given + var employee = _fixture.Build() + .Without(x => x.LeadingDepartments) + .Without(x => x.Department) + .With(x => x.EmpNo, empId) + .Create(); + _dbContextMock.Setup(x => x.Employees).Returns(new[] { employee }.GetMockDbSetObject()); + + //when + var emp = await _repository.GetByIdAsync(empId, default); + + //then + emp.Id.Should().Be(empId); + } + + [Fact] + public async Task GetOldestAsync_should_return_expected_employee() + { + //given + var employees = _fixture.Build() + .Without(x => x.LeadingDepartments) + .Without(x => x.Department) + .CreateMany(20); + + _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); + + //when + var emp = await _repository.GetOldestAsync(default); + + //then + var theOldest = employees.OrderBy(x => x.BirthDate).First(); + + emp.Id.Should().Be(theOldest.EmpNo); + emp.LastName.Should() + .NotBeNullOrEmpty() + .And.Be(theOldest.LastName); + } + + [Fact] + public async Task DeleteByIdAsync_should_return_false_when_employee_not_found() + { + //given + _dbContextMock.Setup(x => x.Employees).Returns(Enumerable.Empty().GetMockDbSetObject); + + //when + var result = await _repository.DeleteByIdAsync(99, default); + + //then + result.Should().Be(false); + + _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never); + } + + [Theory, AutoData] + public async Task DeleteByIdAsync_should_return_true_and_save_when_employee_found(int empId) + { + //given + var employees = _fixture.Build() + .Without(x => x.LeadingDepartments) + .Without(x => x.Department) + .With(x => x.EmpNo, empId) + .CreateMany(1); + + _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); + + _dbContextMock.Setup(x => x.SaveChangesAsync(It.IsAny())) + .ReturnsAsync(1); + + //when + var result = await _repository.DeleteByIdAsync(empId, default); + + //then + result.Should().Be(true); + + _dbContextMock.Verify(x => x.Employees.Remove(It.Is(y => y.EmpNo == empId)), Times.Once); + _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); + } + + [Theory, AutoData] + public async Task InsertAsync_should_add_and_save(EmployeePostDto employeePostDto) + { + //given + var employees = _fixture.Build() + .Without(x => x.LeadingDepartments) + .Without(x => x.Department) + .CreateMany(3); + + _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); + + //when + var result = await _repository.InsertAsync(employeePostDto, default); + + //then + result.Should().BeEquivalentTo(employeePostDto); + + _dbContextMock.Verify(x => x.Employees.AddAsync(It.Is(y => y.LastName == employeePostDto.LastName), It.IsAny()), Times.Once); + _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); + } + + [Theory, AutoData] + public async Task UpdateAsync_should_return_null_when_employee_not_found(int empId, EmployeePutDto employeePutDto) + { + //given + _dbContextMock.Setup(x => x.Employees).Returns(Enumerable.Empty().GetMockDbSetObject); + + //when + var result = await _repository.UpdateAsync(empId, employeePutDto, default); + + //then + result.Should().BeNull(); + + _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never); + } + + [Theory, AutoData] + public async Task UpdateAsync_should_update_the_entity_and_save_when_employee_found(int empId, EmployeePutDto employeePutDto) + { + //given + var employees = _fixture.Build() + .Without(x => x.LeadingDepartments) + .Without(x => x.Department) + .With(x => x.EmpNo, empId) + .CreateMany(1); + + _dbContextMock.Setup(x => x.Employees).Returns(employees.GetMockDbSetObject()); + + //when + var result = await _repository.UpdateAsync(empId, employeePutDto, default); + + //then + result.LastName.Should().Be(employeePutDto.LastName); + + _dbContextMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); + } + } +} diff --git a/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Services/CarServiceTests.cs b/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Services/CarServiceTests.cs index 021e77ae3..436ef4fd5 100644 --- a/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Services/CarServiceTests.cs +++ b/test/HappyCode.NetCoreBoilerplate.Core.UnitTests/Services/CarServiceTests.cs @@ -1,50 +1,50 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AutoFixture; -using AutoFixture.Xunit2; -using FluentAssertions; -using HappyCode.NetCoreBoilerplate.Core.Models; -using HappyCode.NetCoreBoilerplate.Core.Services; -using HappyCode.NetCoreBoilerplate.Core.UnitTests.Extensions; -using Microsoft.EntityFrameworkCore; -using Moq; -using Xunit; - -namespace HappyCode.NetCoreBoilerplate.Core.UnitTests.Services -{ - public class CarServiceTests - { - private static readonly Fixture _fixture = new Fixture(); - - private readonly CarService _service; - private readonly Mock _dbContextMock; - - public CarServiceTests() - { - _dbContextMock = new Mock(new DbContextOptionsBuilder().Options); - - _service = new CarService(_dbContextMock.Object); - } - - [Theory, AutoData] - public async Task GetAllSortedByPlateAsync_should_return_expected_result(int rand1, int rand2, int expectedId) - { - //given - _fixture.Customize(c => c.Without(x => x.Owner)); - - var cars = new List(); - _fixture.AddManyTo(cars, rand1); - cars.Add(new Car { Id = expectedId, Plate = "0" }); - _fixture.AddManyTo(cars, rand2); - - _dbContextMock.Setup(x => x.Cars).Returns(cars.GetMockDbSetObject()); - - //when - var result = await _service.GetAllSortedByPlateAsync(default); - - //then - result.First().Id.Should().Be(expectedId); - } - } -} +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.Xunit2; +using FluentAssertions; +using HappyCode.NetCoreBoilerplate.Core.Models; +using HappyCode.NetCoreBoilerplate.Core.Services; +using HappyCode.NetCoreBoilerplate.Core.UnitTests.Extensions; +using Microsoft.EntityFrameworkCore; +using Moq; +using Xunit; + +namespace HappyCode.NetCoreBoilerplate.Core.UnitTests.Services +{ + public class CarServiceTests + { + private static readonly Fixture _fixture = new Fixture(); + + private readonly CarService _service; + private readonly Mock _dbContextMock; + + public CarServiceTests() + { + _dbContextMock = new Mock(new DbContextOptionsBuilder().Options); + + _service = new CarService(_dbContextMock.Object); + } + + [Theory, AutoData] + public async Task GetAllSortedByPlateAsync_should_return_expected_result(int rand1, int rand2, int expectedId) + { + //given + _fixture.Customize(c => c.Without(x => x.Owner)); + + var cars = new List(); + _fixture.AddManyTo(cars, rand1); + cars.Add(new Car { Id = expectedId, Plate = "0" }); + _fixture.AddManyTo(cars, rand2); + + _dbContextMock.Setup(x => x.Cars).Returns(cars.GetMockDbSetObject()); + + //when + var result = await _service.GetAllSortedByPlateAsync(default); + + //then + result.First().Id.Should().Be(expectedId); + } + } +}