Repository: natenho/Mockaco Branch: master Commit: 168cbbe5663d Files: 215 Total size: 312.2 KB Directory structure: gitextract_rnbuvyzp/ ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── 01_feature_request.yml │ │ ├── 02_bug_report.yml │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE/ │ │ └── pull_request_template.md │ └── workflows/ │ ├── main-release.yml │ ├── website-deploy-test.yml │ └── website-deploy.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Mockaco.sln ├── README.md ├── appveyor.yml ├── checkBuild.ps1 ├── src/ │ ├── Mockaco/ │ │ ├── .dockerignore │ │ ├── Docker/ │ │ │ ├── Dockerfile │ │ │ └── README.md │ │ ├── Extensions/ │ │ │ └── CommandLineExtensions.cs │ │ ├── Mockaco.csproj │ │ ├── Mocks/ │ │ │ └── hello.json │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ ├── PublishProfiles/ │ │ │ │ └── FolderProfile.pubxml │ │ │ └── launchSettings.json │ │ ├── Settings/ │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.Docker.json │ │ │ ├── appsettings.Production.json │ │ │ └── appsettings.json │ │ ├── Startup.Banner.cs │ │ └── Startup.cs │ └── Mockaco.AspNetCore/ │ ├── Chaos/ │ │ └── Strategies/ │ │ ├── ChaosStrategyBehavior.cs │ │ ├── ChaosStrategyException.cs │ │ ├── ChaosStrategyLatency.cs │ │ ├── ChaosStrategyResult.cs │ │ ├── ChaosStrategyTimeout.cs │ │ └── IChaosStrategy.cs │ ├── Common/ │ │ ├── HttpContentTypes.cs │ │ ├── HttpHeaders.cs │ │ ├── InvalidMockException.cs │ │ ├── RouteMatcher.cs │ │ ├── SimpleExceptionConverter.cs │ │ └── StringDictionary.cs │ ├── DependencyInjection/ │ │ ├── MockacoApplicationBuilder.cs │ │ └── MockacoServiceCollection.cs │ ├── Extensions/ │ │ ├── EnumerableExtensions.cs │ │ ├── HttpRequestExtensions.cs │ │ ├── ObjectExtensions.cs │ │ ├── StringDictionaryExtensions.cs │ │ └── StringExtensions.cs │ ├── HealthChecks/ │ │ └── StartupHealthCheck.cs │ ├── IMockProvider.cs │ ├── IMockacoContext.cs │ ├── InternalsVisibleTo.cs │ ├── Middlewares/ │ │ ├── CallbackMiddleware.cs │ │ ├── ChaosMiddleware.cs │ │ ├── ErrorHandlingMiddleware.cs │ │ ├── RequestMatchingMiddleware.cs │ │ ├── ResponseDelayMiddleware.cs │ │ └── ResponseMockingMiddleware.cs │ ├── MockProvider.cs │ ├── Mockaco.AspNetCore.csproj │ ├── MockacoContext.cs │ ├── Options/ │ │ ├── ChaosOptions.cs │ │ ├── MockacoOptions.cs │ │ └── TemplateFileProviderOptions.cs │ ├── Plugins/ │ │ └── PhoneNumberExtensions.cs │ ├── PublicAPI.Shipped.txt │ ├── PublicAPI.Unshipped.txt │ ├── Settings/ │ │ └── VerificationRouteValueTransformer.cs │ ├── Templating/ │ │ ├── Generating/ │ │ │ ├── Cli/ │ │ │ │ └── GeneratorRunner.cs │ │ │ ├── GeneratedTemplate.cs │ │ │ ├── GeneratingOptions.cs │ │ │ ├── Providers/ │ │ │ │ ├── GeneratedTemplateProviderFactory.cs │ │ │ │ ├── IGeneratedTemplateProvider.cs │ │ │ │ ├── IGeneratedTemplateProviderFactory.cs │ │ │ │ └── OpenApiTemplateProvider.cs │ │ │ ├── ServiceCollectionExtensions.cs │ │ │ ├── Source/ │ │ │ │ ├── HttpContentProvider.cs │ │ │ │ ├── ISourceContentProvider.cs │ │ │ │ ├── LocalFileContentProvider.cs │ │ │ │ └── SourceContentProviderComposite.cs │ │ │ ├── Store/ │ │ │ │ ├── GeneratedTemplateStore.cs │ │ │ │ ├── IGeneratedTemplateStore.cs │ │ │ │ └── TemplateStoreOptions.cs │ │ │ └── TemplatesGenerator.cs │ │ ├── ITemplateTransformer.cs │ │ ├── Models/ │ │ │ ├── CallbackTemplate.cs │ │ │ ├── Error.cs │ │ │ ├── IRawTemplate.cs │ │ │ ├── Mock.cs │ │ │ ├── RawTemplate.cs │ │ │ ├── RequestTemplate.cs │ │ │ ├── ResponseTemplate.cs │ │ │ └── Template.cs │ │ ├── Providers/ │ │ │ ├── ITemplateProvider.cs │ │ │ └── TemplateFileProvider.cs │ │ ├── Request/ │ │ │ ├── FormRequestBodyStrategy.cs │ │ │ ├── IRequestBodyFactory.cs │ │ │ ├── IRequestBodyStrategy.cs │ │ │ ├── IRequestMatcher.cs │ │ │ ├── JsonRequestBodyStrategy.cs │ │ │ ├── RequestBodyFactory.cs │ │ │ ├── RequestConditionMatcher.cs │ │ │ ├── RequestMethodMatcher.cs │ │ │ ├── RequestRouteMatcher.cs │ │ │ └── XmlRequestBodyStrategy.cs │ │ ├── Response/ │ │ │ ├── BinaryResponseBodyStrategy.cs │ │ │ ├── DefaultResponseBodyStrategy.cs │ │ │ ├── IResponseBodyFactory.cs │ │ │ ├── IResponseBodyStrategy.cs │ │ │ ├── JsonResponseBodyStrategy.cs │ │ │ ├── ResponseBodyFactory.cs │ │ │ ├── StringResponseBodyStrategy.cs │ │ │ └── XmlResponseBodyStrategy.cs │ │ ├── Scripting/ │ │ │ ├── IFakerFactory.cs │ │ │ ├── IGlobalVarialeStorage.cs │ │ │ ├── IScriptContext.cs │ │ │ ├── IScriptRunnerFactory.cs │ │ │ ├── LocalizedFakerFactory.cs │ │ │ ├── ScriptContext.cs │ │ │ ├── ScriptContextGlobalVariableStorage.cs │ │ │ ├── ScriptContextRequest.cs │ │ │ ├── ScriptContextResponse.cs │ │ │ └── ScriptRunnerFactory.cs │ │ ├── T4/ │ │ │ ├── TemplateSegment.cs │ │ │ └── Tokeniser.cs │ │ └── TemplateTransformer.cs │ ├── Verifyer/ │ │ └── VerifyerExtensions.cs │ └── WarmUps/ │ └── MockProviderWarmUp.cs ├── test/ │ ├── Mockaco.AspNetCore.Tests/ │ │ ├── Common/ │ │ │ └── StringDictionaryTest.cs │ │ ├── Extensions/ │ │ │ ├── EnumerableExtensionTests.cs │ │ │ └── StringExtensionsTests.cs │ │ ├── Middlewares/ │ │ │ ├── CallbackMiddlewareTest.cs │ │ │ ├── ErrorHandlingMiddlewareTest.cs │ │ │ ├── RequestMatchingMiddlewareTest.cs │ │ │ ├── ResponseDelayMiddlewareTest.cs │ │ │ └── ResponseMockingMiddlewareTest.cs │ │ ├── Mockaco.AspNetCore.Tests.csproj │ │ ├── Templating/ │ │ │ ├── Request/ │ │ │ │ ├── JsonRequestBodyStrategyTest.cs │ │ │ │ └── RequestConditionMatcherTest.cs │ │ │ ├── Response/ │ │ │ │ ├── BinaryResponseBodyStrategyTest.cs │ │ │ │ ├── DefaultResponseBodyStrategyTest.cs │ │ │ │ ├── JsonResponseBodyStrategyTest.cs │ │ │ │ ├── ResponseBodyFactoryTest.cs │ │ │ │ └── XmlResponseBodyStrategyTest.cs │ │ │ ├── Scripting/ │ │ │ │ ├── LocalizedFakerFactoryTest.cs │ │ │ │ └── ScriptRunnerFactoryTest.cs │ │ │ ├── TemplateTransformerTest.cs │ │ │ ├── Transforms_Plain_Json_Template.json │ │ │ └── Transforms_Scripted_Json_Template.json │ │ ├── TextFileDataAttribute.cs │ │ └── Usings.cs │ └── _postman/ │ └── Mockaco.postman_collection.json └── website/ ├── .gitignore ├── README.md ├── babel.config.js ├── blog/ │ └── authors.yml ├── docs/ │ ├── chaos/ │ │ └── index.md │ ├── configuration/ │ │ ├── _category_.json │ │ └── index.md │ ├── generator/ │ │ ├── _category_.json │ │ └── index.md │ ├── guides/ │ │ ├── _category_.json │ │ ├── mocking-raw.md │ │ ├── mocking-stateful.md │ │ └── mocking-xml.md │ ├── health-checks/ │ │ ├── _category_.json │ │ └── index.md │ ├── quick-start/ │ │ ├── _category_.json │ │ ├── create-mock.md │ │ ├── install-run.md │ │ └── test-mock.md │ ├── reference-scripting/ │ │ ├── _category_.json │ │ ├── faker.md │ │ ├── global.md │ │ ├── index.md │ │ ├── request.md │ │ └── response.md │ ├── reference-template/ │ │ ├── _category_.json │ │ ├── callback/ │ │ │ ├── _category_.json │ │ │ ├── body-attribute.md │ │ │ ├── delay-attribute.md │ │ │ ├── headers-attribute.md │ │ │ ├── method-attribute.md │ │ │ ├── timeout-attribute.md │ │ │ └── url-attribute.md │ │ ├── request/ │ │ │ ├── _category_.json │ │ │ ├── condition-attribute.md │ │ │ ├── method-attribute.md │ │ │ └── route-attribute.md │ │ └── response/ │ │ ├── _category_.json │ │ ├── body-attribute.md │ │ ├── delay-attribute.md │ │ ├── file-attribute.md │ │ ├── headers-attribute.md │ │ ├── indented-attribute.md │ │ └── status-attribute.md │ ├── request-matching/ │ │ ├── _category_.json │ │ └── index.md │ └── verification/ │ ├── _category_.json │ └── index.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src/ │ ├── components/ │ │ └── HomepageFeatures/ │ │ ├── index.js │ │ └── styles.module.css │ ├── css/ │ │ └── custom.css │ └── pages/ │ ├── index.js │ ├── index.module.css │ ├── videos.js │ └── videos.module.css └── static/ └── .nojekyll ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # 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 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX 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 # 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 # 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 # TODO: 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/* *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable 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 # 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 node_modules/ 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 # 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 # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # 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 # Mockaco-specific **/.dockerignore **/*.md .github/ nupkg/ test/ website/ **/*.log src/Mockaco/Mocks*/**/*.* !src/Mockaco/Mocks/hello.json ================================================ FILE: .editorconfig ================================================ [*.{cs,vb}] 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 = _ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .github/ISSUE_TEMPLATE/01_feature_request.yml ================================================ --- name: 🚀 Feature request description: Suggest an idea for this project labels: ["enhancement"] body: - type: checkboxes id: prerequisites attributes: label: Prerequisites options: - label: I have written a descriptive issue title required: true - label: I have searched existing issues to ensure a similar issue has not already been created required: true - type: textarea id: context attributes: label: Description description: Is your feature request related to a problem? Please describe. placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] validations: required: true - type: textarea id: proposed_solution attributes: label: Proposed solution description: Describe the solution you'd like. placeholder: A clear and concise description of what you want to happen. validations: required: true - type: textarea id: alternatives attributes: label: Alternatives description: Describe alternatives you've considered. placeholder: A clear and concise description of any alternative solutions or features you've considered. validations: required: true - type: textarea id: additional_context attributes: label: Additional context description: Add any other context or screenshots about the feature request here. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/02_bug_report.yml ================================================ --- name: 🐛 Bug report description: Create a report to help us improve labels: ["bug"] body: - type: checkboxes id: prerequisites attributes: label: Prerequisites options: - label: I have written a descriptive issue title required: true - label: I have searched existing bugs to ensure a similar bug has not already been created required: true - type: textarea id: description attributes: label: Description description: A clear and concise description of what the bug is. validations: required: true - type: textarea id: steps_to_reproduce attributes: label: Steps to reproduce description: You can attach the mock reproducing the wrong behavior or even the configuration files you are using. validations: required: true - type: textarea id: expected_behavior attributes: label: Expected behavior description: A clear and concise description of what you expected to happen. validations: required: true - type: textarea id: screenshots attributes: label: Screenshots description: If applicable, add screenshots to help explain your problem. validations: required: false - type: textarea id: additional_context attributes: label: Additional context description: Add any other context about the problem here. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false ================================================ FILE: .github/PULL_REQUEST_TEMPLATE/pull_request_template.md ================================================ *Thank you for your contribution to the Mockaco 🐵 repo!* Before submitting this PR, please make sure: - [ ] There is an open issue related to your change - [ ] There aren't other open pull request for the same update/change - [ ] You have followed the project guidelines - [ ] Your code builds clean without any errors or warnings - [ ] You have added unit tests Please provide a description of your changes and why you'd like us to include them. ================================================ FILE: .github/workflows/main-release.yml ================================================ name: Main Release on: workflow_dispatch: push: branches: - master pull_request: branches: - '**' env: DOCKER_HUB_USERNAME: natenho jobs: build: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, 'skip-ci')" steps: - name: Checkout code uses: actions/checkout@v1 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v1 with: dotnet-version: '6.0.x' - name: Setup GitVersion uses: gittools/actions/gitversion/setup@v0.9.7 with: versionSpec: '5.x' - name: Determine version id: gitversion uses: gittools/actions/gitversion/execute@v0.9.7 - name: Restore dependencies run: dotnet restore --verbosity normal - name: Build run: dotnet build --configuration Release --verbosity normal src/Mockaco/Mockaco.csproj - name: Test run: dotnet test --configuration Release --verbosity normal test/Mockaco.AspNetCore.Tests/Mockaco.AspNetCore.Tests.csproj - name: Bump version and push tag if: "github.ref == 'refs/heads/master' && !contains(github.event.head_commit.message, 'skip-release')" id: tag_version uses: mathieudutour/github-tag-action@v5.6 with: custom_tag: ${{ steps.gitversion.outputs.semVer }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Create a GitHub release if: "github.ref == 'refs/heads/master'" id: github_release uses: ncipollo/release-action@v1 with: tag: v${{ steps.gitversion.outputs.semVer }} name: v${{ steps.gitversion.outputs.semVer }} body: ${{ steps.tag_version.outputs.changelog }} - name: Create nupkg run: dotnet pack --configuration Nuget --output ./nupkg - name: Publish nupkg if: "github.ref == 'refs/heads/master' && !contains(github.event.head_commit.message, 'skip-nuget')" run: dotnet nuget push **/*.nupkg --api-key ${{secrets.NUGET_AUTH_TOKEN}} --source https://api.nuget.org/v3/index.json --skip-duplicate - name: Cache Docker layers uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx- - name: Login to Docker Hub uses: docker/login-action@v1 if: "github.ref == 'refs/heads/master'" with: username: ${{ env.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Setup Docker Build id: buildx uses: docker/setup-buildx-action@v2 - name: Docker Build id: docker_build uses: docker/build-push-action@v2 with: context: ./ file: ./src/Mockaco/Docker/Dockerfile push: ${{ github.ref == 'refs/heads/master' && !contains(github.event.head_commit.message, 'skip-docker') }} tags: ${{ env.DOCKER_HUB_USERNAME }}/mockaco:latest,${{ env.DOCKER_HUB_USERNAME }}/mockaco:${{ steps.gitversion.outputs.semVer }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache platforms: linux/amd64,linux/arm64 - name: Docker Image digest run: echo ${{ steps.docker_build.outputs.digest }} ================================================ FILE: .github/workflows/website-deploy-test.yml ================================================ name: Deploy to GitHub Pages (Test) on: pull_request: branches: - master defaults: run: working-directory: ./website jobs: test-deploy: name: Test deployment runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 cache: yarn cache-dependency-path: '**/yarn.lock' - name: Install dependencies run: yarn install --frozen-lockfile - name: Test build website run: yarn build ================================================ FILE: .github/workflows/website-deploy.yml ================================================ name: Deploy to GitHub Pages on: push: branches: - master # Review gh actions docs if you want to further define triggers, paths, etc # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on defaults: run: working-directory: ./website jobs: deploy: name: Deploy to GitHub Pages runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 cache: yarn cache-dependency-path: '**/yarn.lock' - name: Install dependencies run: yarn install --frozen-lockfile - name: Build website run: yarn build # Popular action to deploy to GitHub Pages: # Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} # Build output to publish to the `gh-pages` branch: publish_dir: ./website/build # The following lines assign commit authorship to the official # GH-Actions bot for deploys to `gh-pages` branch: # https://github.com/actions/checkout/issues/13#issuecomment-724415212 # The GH actions bot is used by default if you didn't specify the two fields. # You can swap them out with your own user credentials. user_name: github-actions[bot] user_email: 41898282+github-actions[bot]@users.noreply.github.com ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # 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 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX 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 # 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 # 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 # TODO: 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/* *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable 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 # 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 node_modules/ 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 # 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 # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # 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 # Mock Files src/Mockaco/Mocks*/**/*.* !src/Mockaco/Mocks/hello.json /.vscode ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at natenho@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: CONTRIBUTING.md ================================================ ## How to contribute to Mockaco It's great you're reading this, because we need volunteer developers to help this project to move on! Please take care of reading existent code to follow the same conventions and use clean code intentions when writing new code. Consider the people who will read your code, and make it look nice for them :-). #### **Did you find a bug?** * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/natenho/mockaco/issues). * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/natenho/mockaco/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **mock template** or an **executable test case** demonstrating the expected behavior that is not occurring. #### **Did you write a patch that fixes a bug?** * Open a new GitHub pull request with the patch. * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. #### **Do you intend to add a new feature or change an existing one?** * Please [open a new issue](https://github.com/natenho/mockaco/issues/new) describing the feature so it can be discussed before you start writing it. #### **Do you have any other questions?** * Simply [open a new issue](https://github.com/natenho/mockaco/issues/new). Thanks! Mockaco Team 🐵 ================================================ FILE: LICENSE ================================================ Mockaco - Copyright (c) 2019-2021 Renato Lima Licensed under the Apache License, Version 2.0 (the "License"); you may not use this source code except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -- Serilog is copyright © 2013-2020 Serilog Contributors All Rights Reserved xUnit Copyright (c) .NET Foundation and Contributors All Rights Reserved Fluent Assertions © 2021 Dennis Doomen -- Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS -- The MIT License (MIT) MAB.DotIgnore - Copyright (c) 2016 Mark Ashley Bell Bogus - Copyright (c) 2015 Brian Chavez GitVersion - Copyright (c) 2013 NServiceBus Ltd Moq - Copyright (c) Daniel Cazzulino and Contributors Newtonsoft.Json - Copyright (c) 2007 James Newton-King Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- New BSD License Polly - Copyright (c) 2015-2020, App vNext All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of App vNext nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Mockaco.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32210.238 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mockaco", "src\Mockaco\Mockaco.csproj", "{8BAA1EC5-0BF5-4DA2-87F7-ED0C7B652517}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3F7B6722-59F1-4943-8D45-94D42CE49639}" ProjectSection(SolutionItems) = preProject README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mockaco.AspNetCore.Tests", "test\Mockaco.AspNetCore.Tests\Mockaco.AspNetCore.Tests.csproj", "{EE57B1B4-29D2-4AE3-8F23-5E622302C30F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mockaco.AspNetCore", "src\Mockaco.AspNetCore\Mockaco.AspNetCore.csproj", "{7766C592-9887-4162-8B9C-51003ED30335}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Nuget|Any CPU = Nuget|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8BAA1EC5-0BF5-4DA2-87F7-ED0C7B652517}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8BAA1EC5-0BF5-4DA2-87F7-ED0C7B652517}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BAA1EC5-0BF5-4DA2-87F7-ED0C7B652517}.Nuget|Any CPU.ActiveCfg = Nuget|Any CPU {8BAA1EC5-0BF5-4DA2-87F7-ED0C7B652517}.Nuget|Any CPU.Build.0 = Nuget|Any CPU {8BAA1EC5-0BF5-4DA2-87F7-ED0C7B652517}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BAA1EC5-0BF5-4DA2-87F7-ED0C7B652517}.Release|Any CPU.Build.0 = Release|Any CPU {EE57B1B4-29D2-4AE3-8F23-5E622302C30F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE57B1B4-29D2-4AE3-8F23-5E622302C30F}.Debug|Any CPU.Build.0 = Debug|Any CPU {EE57B1B4-29D2-4AE3-8F23-5E622302C30F}.Nuget|Any CPU.ActiveCfg = Release|Any CPU {EE57B1B4-29D2-4AE3-8F23-5E622302C30F}.Release|Any CPU.ActiveCfg = Debug|Any CPU {7766C592-9887-4162-8B9C-51003ED30335}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7766C592-9887-4162-8B9C-51003ED30335}.Debug|Any CPU.Build.0 = Debug|Any CPU {7766C592-9887-4162-8B9C-51003ED30335}.Nuget|Any CPU.ActiveCfg = Release|Any CPU {7766C592-9887-4162-8B9C-51003ED30335}.Nuget|Any CPU.Build.0 = Release|Any CPU {7766C592-9887-4162-8B9C-51003ED30335}.Release|Any CPU.ActiveCfg = Release|Any CPU {7766C592-9887-4162-8B9C-51003ED30335}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CABB6019-182D-4F69-834A-5CF7921C290F} EndGlobalSection EndGlobal ================================================ FILE: README.md ================================================

Mockaco

# Mockaco [![Main Build](https://github.com/natenho/Mockaco/actions/workflows/main-release.yml/badge.svg)](https://github.com/natenho/Mockaco/actions/workflows/main-release.yml) [![Docker Pulls](https://img.shields.io/docker/pulls/natenho/mockaco)](https://hub.docker.com/repository/docker/natenho/mockaco) [![Nuget](https://img.shields.io/nuget/dt/Mockaco?color=blue&label=nuget%20downloads)](https://www.nuget.org/packages/Mockaco/) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnatenho%2FMockaco.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnatenho%2FMockaco?ref=badge_shield) Mockaco is an HTTP-based API mock server with fast setup. ## Features - **Simple JSON-based Configuration**: Configure your mocks easily using a simple JSON format. - **Pure C# Scripting**: Configure your mocks using C# scripting without the need to learn a new language or API. - **Delay Simulation**: Simulate network delays to test how your system handles timeouts and latency. - **Fake Data Generation**: Generate realistic fake data using the built-in functionality. - **Callback (Webhook) Support**: Trigger another service call when a request hits your mocked API. - **Verification**: Verify if a specific mock was called during testing to ensure expected interactions. - **State Support**: Create stateful mocks that return responses based on global variables previously set by other mocks. - **Portability**: Run the mock server in [any environment supported by .NET](https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md). ## Get Started Access the documentation on [natenho.github.io/Mockaco](https://natenho.github.io/Mockaco/) [![Mocking APIs with Mockaco | .NET 7](https://user-images.githubusercontent.com/4236481/195997781-b730959e-8d6d-432c-b35a-3adb580abc41.png)](https://www.youtube.com/watch?v=QBnXCgZFzM0 "Mocking APIs with Mockaco | .NET 7") ## License [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnatenho%2FMockaco.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnatenho%2FMockaco?ref=badge_large) --- *Icon made by [Freepik](https://www.freepik.com/ "Freepik") from [www.flaticon.com](https://www.flaticon.com/ "Flaticon") is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/ "Creative Commons BY 3.0")* ================================================ FILE: appveyor.yml ================================================ pull_requests: do_not_increment_build_number: true skip_non_tags: true image: Visual Studio 2019 configuration: Release dotnet_csproj: patch: true file: '**\*.csproj' version: '{version}' version_prefix: '{version}' package_version: '{version}' assembly_version: '{version}' file_version: '{version}' informational_version: '{version}' build_script: - cmd: >- dotnet --version dotnet restore ./src/Mockaco/Mockaco.csproj --verbosity m dotnet publish ./src/Mockaco/Mockaco.csproj test_script: - cmd: dotnet test .\test\Mockaco.AspNetCore.Tests\Mockaco.AspNetCore.Tests.csproj artifacts: - path: src\Mockaco\bin\Release\net5.0\publish\ name: Mockaco Web Site deploy: - provider: GitHub tag: v$(appveyor_build_version) auth_token: secure: ksH+zrtlbEnpy6gasfUkZJQrewTVWVKVFPbzTDmhH94Q2SVBMEc4pI6+6I8JaGuE artifact: Mockaco Web Site draft: false force_update: true on: APPVEYOR_REPO_TAG: true - provider: NuGet api_key: secure: DPYxpk2NINisxfFbRST6aH/m0KBYAt9ETmwkMgxRaGqnKtWlFKaZZFuqXtAG4eSj on: APPVEYOR_REPO_TAG: true ================================================ FILE: checkBuild.ps1 ================================================ dotnet restore --verbosity normal dotnet build --configuration Release --verbosity normal .\src\Mockaco\Mockaco.csproj dotnet test --configuration Release --verbosity normal .\test\Mockaco.AspNetCore.Tests\Mockaco.AspNetCore.Tests.csproj dotnet pack --configuration Nuget --output ./nupkg docker build -f ./src/Mockaco/Docker/Dockerfile -t mockaco:local . $containerName = [guid]::NewGuid().ToString() try { docker run --name $containerName -d -p 5000:5000 -v ${PSScriptRoot}/src/Mockaco/Mocks:/app/Mocks mockaco:local Start-Sleep -Seconds 5 docker run --rm -v ${PSScriptRoot}/test/_postman:/etc/newman -t postman/newman:alpine run Mockaco.postman_collection.json } finally { docker container stop $containerName docker container rm $containerName } ================================================ FILE: src/Mockaco/.dockerignore ================================================ **/.classpath **/.dockerignore **/.env **/.git **/.gitignore **/.project **/.settings **/.toolstarget **/.vs **/.vscode **/*.*proj.user **/*.dbmdl **/*.jfm **/azds.yaml **/bin **/charts **/docker-compose* **/Dockerfile* **/node_modules **/npm-debug.log **/obj **/secrets.dev.yaml **/values.dev.yaml LICENSE README.md /Mocks/*.json !/Mocks/hello.json ================================================ FILE: src/Mockaco/Docker/Dockerfile ================================================ FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS base WORKDIR /app EXPOSE 5000 EXPOSE 5443 FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim AS build COPY ./src/Mockaco/Mockaco.csproj /src/Mockaco/ COPY ./src/Mockaco.AspNetCore/Mockaco.AspNetCore.csproj /src/Mockaco.AspNetCore/ WORKDIR /src/Mockaco RUN dotnet restore WORKDIR /repo COPY ./ ./ WORKDIR /repo/src/Mockaco RUN dotnet build "Mockaco.csproj" -c Release -o /app/build RUN find -iname gitversion.json -exec cat {} \; RUN dotnet dev-certs https FROM build AS publish RUN dotnet publish "Mockaco.csproj" -c Release -o /app/publish FROM base AS final ENV DOTNET_USE_POLLING_FILE_WATCHER=true ENV ASPNETCORE_ENVIRONMENT=Docker WORKDIR /app COPY --from=publish /app/publish . COPY ./src/Mockaco/Mocks/hello.json ./Mocks/ COPY ./src/Mockaco/Settings ./Settings COPY --from=build /root/.dotnet/corefx/cryptography/x509stores/my /root/.dotnet/corefx/cryptography/x509stores/my VOLUME /app/Mocks VOLUME /app/Settings VOLUME /root/.dotnet/corefx/cryptography/x509stores/my ENTRYPOINT ["dotnet", "Mockaco.dll"] ================================================ FILE: src/Mockaco/Docker/README.md ================================================ # Quick reference - **Where to get help and to file issues**: [GitHub repository](https://github.com/natenho/Mockaco/) - **Maintained by**: [natenho](https://github.com/natenho) # What is Mockaco? Mockaco is an HTTP-based API mock server with fast setup, featuring: - Simple JSON-based configuration - Pure C# scripting - you don't need to learn a new specific language or API to configure your mocks - Fake data generation - built-in hassle-free fake data generation - Callback support - trigger another service call when a request hits your mocked API logo # How to use this image ## Running the demo The default image ships with a sample "hello" mock: ```console $ docker run -it --rm -p 5000:5000 natenho/mockaco ``` Mockaco can be accessed by any HTTP client via `http://localhost:5000` ```console $ curl -iX GET http://localhost:5000/hello/docker ``` ```http HTTP/1.1 200 OK Date: Wed, 21 Jun 2019 05:10:00 GMT Content-Type: application/json Server: Kestrel Transfer-Encoding: chunked { "message": "Hello docker!" } ``` ## Running and creating your own mocks The best way to use the image is by creating a directory on the host system (outside the container) and mount this to the `/app/Mocks` directory inside the container. 1. Create a data directory on a suitable volume on your host system, e.g. `/my/own/mockdir`. 2. Start your `mockaco` container like this: ```console $ docker run -it --rm -p 5000:80 -v /my/own/mockdir:/app/Mocks natenho/mockaco ``` The `-v /my/own/mockdir:/app/Mocks` part of the command mounts the `/my/own/mockdir` directory from the underlying host system as `/app/Mocks` inside the container, where Mockaco by default will read its mock JSON files. 3. Create a request/response template file named `PingPong.json` under `/my/own/mockdir` folder: ```json { "request": { "method": "GET", "route": "ping" }, "response": { "status": "OK", "body": { "response": "pong" } } } ``` 4. Send a request and get the mocked response, running: ```console $ curl -iX GET http://localhost:5000/ping ``` ```http HTTP/1.1 200 OK Date: Wed, 21 Jun 2019 05:10:00 GMT Content-Type: application/json Server: Kestrel Transfer-Encoding: chunked { "response": "pong" } ``` For advanced usage scenarios, like scripting and fake data generation, refer to the [docs](https://github.com/natenho/Mockaco). ================================================ FILE: src/Mockaco/Extensions/CommandLineExtensions.cs ================================================ namespace System.CommandLine.Parsing { public static class CommandLineExtensions { public static bool IsUsingCommand(this Parser commandLine, string[] args) { var parseResult = commandLine.Parse(args); return parseResult.CommandResult != parseResult.RootCommandResult; } } } ================================================ FILE: src/Mockaco/Mockaco.csproj ================================================  net6.0 latest Linux ..\.. en Debug;Release;Nuget true true mockaco ./nupkg false natenho HTTP mock server, useful to stub services and simulate dynamic API responses, leveraging ASP.NET Core features, built-in fake data generation and pure C# scripting https://github.com/natenho/Mockaco https://github.com/natenho/Mockaco mock http server Apache-2.0 mockaco-icon.png Mockaco True 1701;1702;NU5104 True 1701;1702;NU5104 True 1701;1702;NU5104 all runtime; build; native; contentfiles; analyzers; buildtransitive Always True Always ================================================ FILE: src/Mockaco/Mocks/hello.json ================================================ { "request": { "method": "GET", "route": "hello/{message}" }, "response": { "body": { "id": "<#= Faker.Random.Guid() #>", "message": "Hello <#= Request.Route["message"] #>!", "createdAt": <#= JsonConvert.SerializeObject(System.DateTime.Now) #> } } } ================================================ FILE: src/Mockaco/Program.cs ================================================ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Serilog; using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Builder; using System.CommandLine.Parsing; using System.IO; using System.Reflection; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace Mockaco { public class Program { public static async Task Main(string[] args) { var host = CreateHostBuilder(args).Build(); var commandLine = CreateCommandLineBuilder(args, host) .UseDefaults() .Build(); if (commandLine.IsUsingCommand(args)) { await commandLine.InvokeAsync(args); return; } await host.RunAsync(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureAppConfiguration((_, configuration) => { var assemblyLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); configuration.SetBasePath(Path.Combine(assemblyLocation, "Settings")); var switchMappings = new Dictionary() { {"--path", "Mockaco:TemplateFileProvider:Path" }, {"--logs", "Serilog:WriteTo:0:Args:path" } }; configuration.AddCommandLine(args, switchMappings); }) .UseStartup(); }) .UseSerilog((context, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(context.Configuration)); private static CommandLineBuilder CreateCommandLineBuilder(string[] args, IHost host) { var rootCommand = new RootCommand(); foreach (var cmd in host.Services.GetServices()) { rootCommand.AddCommand(cmd); } return new CommandLineBuilder(rootCommand); } } } ================================================ FILE: src/Mockaco/Properties/PublishProfiles/FolderProfile.pubxml ================================================ FileSystem FileSystem Release Any CPU True False 8baa1ec5-0bf5-4da2-87f7-ed0c7b652517 publish True ================================================ FILE: src/Mockaco/Properties/launchSettings.json ================================================ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { "Mockaco": { "commandName": "Project", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:5000" } } } ================================================ FILE: src/Mockaco/Settings/appsettings.Development.json ================================================ { "Mockaco": { "TemplateFileProvider": { "Path": "Mocks" } }, "Serilog": { "WriteTo": [ { }, { "Name": "File", "Args": { "path": "Logs/Mockaco_.log", "rollingInterval": "Day", "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {RequestId} {Message:lj}{NewLine}{Exception}" } } ] } } ================================================ FILE: src/Mockaco/Settings/appsettings.Docker.json ================================================ { "Urls": "http://+:5000;https://+:5443", "Mockaco": { "TemplateFileProvider": { "Path": "Mocks" } } } ================================================ FILE: src/Mockaco/Settings/appsettings.Production.json ================================================ { "Urls": "http://127.0.0.1:0", "Serilog": { "WriteTo": [ { }, { "Name": "File", "Args": { "path": "Logs/Mockaco_.log", "rollingInterval": "Day", "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {RequestId} {Message:lj}{NewLine}{Exception}" } } ] } } ================================================ FILE: src/Mockaco/Settings/appsettings.json ================================================ { "Mockaco": { "DefaultHttpStatusCode": "OK", "ErrorHttpStatusCode": "NotImplemented", "DefaultHttpContentType": "application/json", "References": [], "VerificationIgnoredHeaders": [ "Accept", "Connection", "Host", "User-Agent", "Accept-Encoding", "Postman-Token", "Content-Type", "Content-Length" ], "Imports": [], "MatchedRoutesCacheDuration": 60, "MockacoEndpoint": "_mockaco", "VerificationEndpointName": "verification", "Chaos": { "Enabled": false, "ChaosRate": 20, "MinimumLatencyTime": 500, "MaximumLatencyTime": 3000, "TimeBeforeTimeout": 10000 } }, "AllowedHosts": "*", "Serilog": { "MinimumLevel": { "Default": "Debug", "Override": { "Microsoft": "Information", "System": "Information" } }, "WriteTo": [ { "Name": "Console", "Args": { "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Literate, Serilog.Sinks.Console", "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {RequestId} {Message:lj}{NewLine}{Exception}" } } ] } } ================================================ FILE: src/Mockaco/Startup.Banner.cs ================================================ namespace Mockaco { public partial class Startup { private const string _logo = " .x000000000000000000000d. \n" + " .kMW0OkdooooooooooooooookNWd. \n" + " ... .kMNOdoc;;;;;;;;;;;;;;;;;;;:xNWx. \n" + " lNMWM0' kMNOdoc;;;;;;;;;;;;;;;;;;;;;;;:xNWl \n" + " .MMo;OMx :xxkWM0ddlcOKKKKKKKKKKKKKKKKKKKOc;;;kMNxxx: \n" + " .MMc;kMx XMOloWM0ddKWNdllcccccccccccccccdNWx;;kMXoo0MO\n" + " .MMc;kMx .WMc..XM0ddNMd...... . ......dM0;;kMO..lMK\n" + " oMWc;kMx .WMc..XM0ddNMd...... . ......dM0;;kMO..lMK\n" + " cOOOOOOOOOOOOOOONWOc;oXMo .WMl..XM0ddXMK:,,'..............:KMO;;kMO..oMK\n" + " lNMKOOxddddddddddddc;dXMO' lXWNNMM0ddodXWWWMMx.......0MMWWWXo;;;kMWNNWX;\n" + " lNWKxdkOkkkkkkkkkkkkkOXWk' ..'NM0ddl;;:::0Mk.......0MO:::;;;;;kM0... \n" + " NMXxdkXM0ddddddddddddddo. XM0ddl;;;;;OMk.......0MO;;;;;;;;kMO \n" + " WMOdOMN, .,,,,,,,,,,,,,,,,,, OMNkdo:;;;;dWWkoolllkWNd;;;;;;:dXMd \n" + " WMOdOMK ;0MNXXXXXXXXXXXXXXXXX:'''oXMXkdo:;;;:kKKKKKKKx:;;;;;:dXMO, \n" + " WMOdOMK .lXW0l;;;;;;;;;;;;;;;;;;;;;;;;oXMNOkxllllllllllllllllldXMk. \n" + " WMOdOMK .dWNOc;;;;;;;;;;;;;;;;;;;;;;;;;;;;oK000000000000000000000k. \n" + " WMOdOMK .WMk:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;. \n" + " WMOdkWWdoMN:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;lOl \n" + " kWW0xdONMM0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;xM0 \n" + " .dWWKxdkXx;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;xM0 \n" + " .dNWKxdl:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;xM0 \n" + " oNWKdol:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;lKMk \n" + " oMXddd:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cOMWl \n" + " OMKddo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:kWW0xc \n" + " NMOddl;;;;;loooooooooooooooooooooooo:;;;;;;;;loxNMKxdloXK \n" + " 'MMkddc;;;;oWWKNNNNXXXXXXK000000000WMd;;;;;;;;KMWXxo::;;0MO \n" + " cMWddd:;;;lWMc.odddc;;;;;. kMk;;;;;;;cWMkddo:;;;:KMx \n" + " dMXddo;;;:KMx :dddl;;;;x0c lMK;;;;;;;dMX:dddo:;;;cNMl \n" + " 0M0ddo;;;OMO cNXddd:;;;OMk ;MN:;;;;;;OMk ;dddo:;;;oWW, \n" + " NMOddl;;xMX. 'MWxddl;;;OMk .MMl;;;;;;XMl .xOddo:;;;oWN. \n" + " ,MWxddc;lWW' OM0ddo;;;OMk NMd;;;;;lMM, .XMKddo;;;;kM0 \n" + " lMNkkkooNMl cMNkkkollKMO 0MOlllllkMW. .KM0kkxllloXMk. \n" + " kMWNNNNNNWNl .NMWNNNNNNNWNo. xMWNNNNNNNWNo. .KMMNNNNNNNNNMK; \n" + " XMl......'dNNc kMx.......'dNNc oMK........dNNc .NMd........;OMX' \n" + " 'MM0OOkkkkkkXM0 :MNOOOkkkkkkKM0 :MWOOOkkkkkkKMO .XWOOOkkkkkkkWMl \n" + " :WWWWWWWWWWWWWO .WWWWWWWWWWWWWO ,WWWWWWWWWWWWWk ;WWWWWWWWWWWWWc \n"; } } ================================================ FILE: src/Mockaco/Startup.cs ================================================ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System.Reflection; using System; using System.Linq; namespace Mockaco { public partial class Startup { private readonly IConfiguration _configuration; public Startup(IConfiguration configuration) { _configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services .AddCors() .AddMockaco(_configuration.GetSection("Mockaco")); } public void Configure(IApplicationBuilder app, ILogger logger) { var assemblyName = Assembly.GetExecutingAssembly().GetName().Name; var version = GitVersionInformation.InformationalVersion; var isNoLogoPassed = Environment.GetCommandLineArgs().Contains("--no-logo"); var logMessage = "{assemblyName} v{assemblyVersion} [github.com/natenho/Mockaco]"; if (!isNoLogoPassed) logMessage += "\n\n{logo}"; logger.LogInformation(logMessage, assemblyName, version, _logo); app .UseCors() .UseMockaco(); } } } ================================================ FILE: src/Mockaco.AspNetCore/Chaos/Strategies/ChaosStrategyBehavior.cs ================================================ using System.Net; using System.Text; using Microsoft.AspNetCore.Http; namespace Mockaco.Chaos.Strategies; internal class ChaosStrategyBehavior : IChaosStrategy { public Task Response(HttpResponse httpResponse) { httpResponse.StatusCode = (int)HttpStatusCode.ServiceUnavailable; var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.ServiceUnavailable}"); return httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length, default); } } ================================================ FILE: src/Mockaco.AspNetCore/Chaos/Strategies/ChaosStrategyException.cs ================================================ using System.Net; using System.Text; using Microsoft.AspNetCore.Http; namespace Mockaco.Chaos.Strategies; internal class ChaosStrategyException : IChaosStrategy { public Task Response(HttpResponse httpResponse) { httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError; var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.InternalServerError}"); return httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length); } } ================================================ FILE: src/Mockaco.AspNetCore/Chaos/Strategies/ChaosStrategyLatency.cs ================================================ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Mockaco.Chaos.Strategies; internal class ChaosStrategyLatency : IChaosStrategy { private readonly ILogger _logger; private readonly IOptions _options; public ChaosStrategyLatency(ILogger logger, IOptions options) { _logger = logger; _options = options; } public Task Response(HttpResponse httpResponse) { var responseDelay = new Random().Next(_options.Value.MinimumLatencyTime, _options.Value.MaximumLatencyTime); _logger.LogInformation($"Chaos Latency (ms): {responseDelay}"); return Task.Delay(responseDelay); } } ================================================ FILE: src/Mockaco.AspNetCore/Chaos/Strategies/ChaosStrategyResult.cs ================================================ using System.Net; using System.Text; using Microsoft.AspNetCore.Http; namespace Mockaco.Chaos.Strategies; internal class ChaosStrategyResult : IChaosStrategy { public Task Response(HttpResponse httpResponse) { httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.BadRequest}"); return httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length); } } ================================================ FILE: src/Mockaco.AspNetCore/Chaos/Strategies/ChaosStrategyTimeout.cs ================================================ using System.Net; using System.Text; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; namespace Mockaco.Chaos.Strategies; internal class ChaosStrategyTimeout : IChaosStrategy { private readonly IOptions _options; public ChaosStrategyTimeout(IOptions options) { _options = options; } public async Task Response(HttpResponse httpResponse) { await Task.Delay(_options.Value.TimeBeforeTimeout); httpResponse.StatusCode = (int)HttpStatusCode.RequestTimeout; var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.RequestTimeout}"); await httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length, default); } } ================================================ FILE: src/Mockaco.AspNetCore/Chaos/Strategies/IChaosStrategy.cs ================================================ using Microsoft.AspNetCore.Http; namespace Mockaco.Chaos.Strategies; internal interface IChaosStrategy { Task Response(HttpResponse httpResponse); } ================================================ FILE: src/Mockaco.AspNetCore/Common/HttpContentTypes.cs ================================================ namespace Mockaco { internal static class HttpContentTypes { public const string ApplicationOctetStream = "application/octet-stream"; public const string ApplicationJson = "application/json"; public const string ApplicationXml = "application/xml"; public const string TextXml = "text/xml"; } } ================================================ FILE: src/Mockaco.AspNetCore/Common/HttpHeaders.cs ================================================ namespace Mockaco { internal static class HttpHeaders { public const string ContentType = "Content-Type"; public const string AcceptLanguage = "Accept-Language"; } } ================================================ FILE: src/Mockaco.AspNetCore/Common/InvalidMockException.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; namespace Mockaco.Common { internal class InvalidMockException : Exception { public InvalidMockException() : base() { } public InvalidMockException(string message) : base(message) { } public InvalidMockException(string message, Exception innerException) : base(message, innerException) { } protected InvalidMockException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } ================================================ FILE: src/Mockaco.AspNetCore/Common/RouteMatcher.cs ================================================ using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Template; namespace Mockaco { internal class RouteMatcher { public RouteValueDictionary Match(string routeTemplate, string requestPath) { if(string.IsNullOrWhiteSpace(routeTemplate)) { return null; } var template = TemplateParser.Parse(routeTemplate); var matcher = new TemplateMatcher(template, GetDefaults(template)); var values = new RouteValueDictionary(); return matcher.TryMatch(requestPath, values) ? values : null; } public bool IsMatch(string routeTemplate, string requestPath) { return Match(routeTemplate, requestPath) != null; } private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate) { var result = new RouteValueDictionary(); foreach (var parameter in parsedTemplate.Parameters) { if (parameter.DefaultValue != null) { result.Add(parameter.Name, parameter.DefaultValue); } } return result; } } } ================================================ FILE: src/Mockaco.AspNetCore/Common/SimpleExceptionConverter.cs ================================================ namespace Mockaco.Common { using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; internal class SimpleExceptionConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(Exception).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException("Deserializing exceptions is not supported."); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var exception = value as Exception; if (exception == null) { serializer.Serialize(writer, null); return; } var obj = new JObject { ["Type"] = exception.GetType().Name, ["Message"] = exception.Message, }; if (exception.Data.Count > 0) { obj["Data"] = JToken.FromObject(exception.Data, serializer); } if (exception.InnerException != null) { obj["InnerException"] = JToken.FromObject(exception.InnerException, serializer); } obj.WriteTo(writer); } } } ================================================ FILE: src/Mockaco.AspNetCore/Common/StringDictionary.cs ================================================ using System.Collections.Generic; namespace Mockaco { public class StringDictionary : Dictionary, IReadOnlyDictionary { public new string this[string key] { get { if (TryGetValue(key, out string value)) { return value; } return string.Empty; } set { base[key] = value; } } public new void Add(string key, string value) { Replace(key, value); } public void Replace(string key, string value) { if (ContainsKey(key)) { Remove(key); } base.Add(key, value); } } } ================================================ FILE: src/Mockaco.AspNetCore/DependencyInjection/MockacoApplicationBuilder.cs ================================================ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Mockaco; using Mockaco.Verifyer; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Microsoft.AspNetCore.Builder { public static class MockacoApplicationBuilder { public static IApplicationBuilder UseMockaco(this IApplicationBuilder app, Action configure) { app.UseRouting(); var options = app.ApplicationServices.GetRequiredService>().Value; var optionsChaos = app.ApplicationServices.GetRequiredService>().Value; app.UseEndpoints(endpoints => { endpoints.Map($"/{options.VerificationEndpointPrefix ?? options.MockacoEndpoint}/{options.VerificationEndpointName}", VerifyerExtensions.Verify); endpoints.MapHealthChecks($"/{options.MockacoEndpoint}/ready", new HealthCheckOptions { Predicate = healthCheck => healthCheck.Tags.Contains("ready") }); endpoints.MapHealthChecks($"/{options.MockacoEndpoint}/health", new HealthCheckOptions { Predicate = _ => false }); }); app.UseMiddleware(); configure(app); app .UseMiddleware() .UseMiddleware() .UseMiddleware() .UseMiddleware() .UseMiddleware(); return app; } public static IApplicationBuilder UseMockaco(this IApplicationBuilder app) => app.UseMockaco(_ => { }); } } ================================================ FILE: src/Mockaco.AspNetCore/DependencyInjection/MockacoServiceCollection.cs ================================================ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Options; using Mockaco; using Mockaco.Chaos.Strategies; using Mockaco.HealthChecks; using Mockaco.Settings; namespace Microsoft.Extensions.DependencyInjection { public static class MockacoServiceCollection { public static IServiceCollection AddMockaco(this IServiceCollection services) => services.AddMockaco(_ => { }); public static IServiceCollection AddMockaco(this IServiceCollection services, Action config) => services .AddOptions().Configure(config).Services .AddOptions().Configure>((options, parent) => options = parent.Value.Chaos).Services .AddOptions() .Configure>((options, parent) => options = parent.Value.TemplateFileProvider) .Services .AddCommonServices(); public static IServiceCollection AddMockaco(this IServiceCollection services, IConfiguration config) => services .AddConfiguration(config) .AddCommonServices(); private static IServiceCollection AddConfiguration(this IServiceCollection services, IConfiguration config) => services .AddOptions() .Configure(config) .Configure(config.GetSection("Chaos")) .Configure(config.GetSection("TemplateFileProvider")); private static IServiceCollection AddCommonServices(this IServiceCollection services) { services .AddMemoryCache() .AddHttpClient() .AddInternalServices() .AddChaosServices() .AddHostedService(); services .AddSingleton() .AddHealthChecks() .AddCheck("Startup", tags: new[] { "ready" }); return services; } private static IServiceCollection AddInternalServices(this IServiceCollection services) => services .AddSingleton() .AddScoped() .AddScoped() .AddTransient() .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() .AddScoped() .AddScoped() .AddScoped() .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTemplatesGenerating(); private static IServiceCollection AddChaosServices(this IServiceCollection services) => services .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient(); } } ================================================ FILE: src/Mockaco.AspNetCore/Extensions/EnumerableExtensions.cs ================================================ using System.Threading.Tasks; namespace System.Collections.Generic { internal static class EnumerableExtensions { public static async Task AllAsync(this IEnumerable source, Func> predicate) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); foreach (var item in source) { var result = await predicate(item); if (!result) return false; } return true; } public static async Task AllAsync(this IEnumerable> source, Func predicate) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); foreach (var item in source) { var awaitedItem = await item; if (!predicate(awaitedItem)) return false; } return true; } public static T Random(this IEnumerable enumerable) { int index = new Random().Next(0, enumerable.Count()); return enumerable.ElementAt(index); } } } ================================================ FILE: src/Mockaco.AspNetCore/Extensions/HttpRequestExtensions.cs ================================================ /* The MIT License (MIT) Copyright (c) 2015 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using Microsoft.Net.Http.Headers; using Mockaco; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Microsoft.AspNetCore.Http { /// /// Set of extension methods for Microsoft.AspNetCore.Http.HttpRequest /// internal static class HttpRequestExtensions { private const string UnknownHostName = "UNKNOWN-HOST"; /// /// Gets http request Uri from request object /// /// The /// A New Uri object representing request Uri public static Uri GetUri(this HttpRequest request) { if (null == request) { throw new ArgumentNullException(nameof(request)); } if (string.IsNullOrWhiteSpace(request.Scheme)) { throw new ArgumentException("Http request Scheme is not specified"); } string hostName = request.Host.HasValue ? request.Host.ToString() : UnknownHostName; var builder = new StringBuilder(); builder.Append(request.Scheme) .Append("://") .Append(hostName); if (request.Path.HasValue) { builder.Append(request.Path.Value); } if (request.QueryString.HasValue) { builder.Append(request.QueryString); } return new Uri(builder.ToString()); } public static Routing.RouteValueDictionary GetRouteData(this HttpRequest request, Mock mock) { var routeMatcher = new RouteMatcher(); return routeMatcher.Match(mock.Route, request.Path); } public static bool HasXmlContentType(this HttpRequest request) { MediaTypeHeaderValue.TryParse(request.ContentType, out var parsedValue); return parsedValue?.MediaType.Equals(HttpContentTypes.ApplicationXml, StringComparison.OrdinalIgnoreCase) == true || parsedValue?.MediaType.Equals(HttpContentTypes.TextXml, StringComparison.OrdinalIgnoreCase) == true; } public static async Task ReadBodyStream(this HttpRequest httpRequest) { httpRequest.EnableBuffering(); var encoding = GetEncodingFromContentType(httpRequest.ContentType) ?? Encoding.UTF8; var reader = new StreamReader(httpRequest.Body, encoding); var body = await reader.ReadToEndAsync(); if (httpRequest.Body.CanSeek) { httpRequest.Body.Seek(0, SeekOrigin.Begin); } return body; } public static IEnumerable GetAcceptLanguageValues(this HttpRequest httpRequest) { var acceptLanguages = httpRequest.GetTypedHeaders().AcceptLanguage; if(acceptLanguages == default) { return Enumerable.Empty(); } return acceptLanguages?.Select(l => l.Value.ToString()); } private static Encoding GetEncodingFromContentType(string contentType) { // although the value is well parsed, the encoding is null when it is not informed if (MediaTypeHeaderValue.TryParse(contentType, out var parsedValue)) return parsedValue.Encoding; return null; } } } ================================================ FILE: src/Mockaco.AspNetCore/Extensions/ObjectExtensions.cs ================================================ using Mockaco.Common; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System.Linq; namespace System { internal static class ObjectExtensions { private static readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, Converters = new JsonConverter[] { new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy()}, new SimpleExceptionConverter() }, NullValueHandling = NullValueHandling.Ignore }; public static string ToJson(this T param) where T : class { if (param == null) { return string.Empty; } try { return JsonConvert.SerializeObject(param, _jsonSerializerSettings); } catch { return string.Empty; } } public static bool IsAnyOf(this T item, params T[] possibleItems) { return possibleItems.Contains(item); } } } ================================================ FILE: src/Mockaco.AspNetCore/Extensions/StringDictionaryExtensions.cs ================================================ using Mockaco; namespace System.Collections.Generic { internal static class StringDictionaryExtensions { public static StringDictionary ToStringDictionary( this IEnumerable source, Func keySelector, Func elementSelector) { var dictionary = new StringDictionary(); if (source == null) { return dictionary; } foreach (var item in source) { dictionary.Add(keySelector(item), elementSelector(item)); } return dictionary; } } } ================================================ FILE: src/Mockaco.AspNetCore/Extensions/StringExtensions.cs ================================================ using System.Text; namespace System { internal static class StringExtensions { public static string ToMD5Hash(this string input) { using (Security.Cryptography.MD5 md5 = Security.Cryptography.MD5.Create()) { var inputBytes = Encoding.UTF8.GetBytes(input); var hashBytes = md5.ComputeHash(inputBytes); StringBuilder sb = new StringBuilder(); for (int i = 0; i < hashBytes.Length; i++) { sb.Append(hashBytes[i].ToString("X2")); } return sb.ToString(); } } public static bool IsRemoteAbsolutePath(this string input) { if (Uri.TryCreate(input, UriKind.Absolute, out var uri)) { return !uri.IsFile; } return false; } } } ================================================ FILE: src/Mockaco.AspNetCore/HealthChecks/StartupHealthCheck.cs ================================================ using Microsoft.Extensions.Diagnostics.HealthChecks; namespace Mockaco.HealthChecks { public class StartupHealthCheck : IHealthCheck { private volatile bool _isReady; public bool StartupCompleted { get => _isReady; set => _isReady = value; } public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { if (StartupCompleted) { return Task.FromResult(HealthCheckResult.Healthy("The startup has completed.")); } return Task.FromResult(HealthCheckResult.Unhealthy("That startup is still running.")); } } } ================================================ FILE: src/Mockaco.AspNetCore/IMockProvider.cs ================================================ using System.Collections.Generic; using System.Threading.Tasks; namespace Mockaco { internal interface IMockProvider { List GetMocks(); IEnumerable<(string TemplateName, string ErrorMessage)> GetErrors(); Task WarmUp(); } } ================================================ FILE: src/Mockaco.AspNetCore/IMockacoContext.cs ================================================ using System.Collections.Generic; namespace Mockaco { internal interface IMockacoContext { IScriptContext ScriptContext { get; } Template TransformedTemplate { get; set; } Mock Mock { get; set; } List Errors { get; set; } } } ================================================ FILE: src/Mockaco.AspNetCore/InternalsVisibleTo.cs ================================================ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Mockaco.AspNetCore.Tests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] ================================================ FILE: src/Mockaco.AspNetCore/Middlewares/CallbackMiddleware.cs ================================================ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; namespace Mockaco { internal class CallbackMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public CallbackMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public Task Invoke( HttpContext httpContext, IMockacoContext mockacoContext, IScriptContext scriptContext, ITemplateTransformer templateTransformer, IOptionsSnapshot options) { if (mockacoContext.TransformedTemplate?.Callbacks?.Any() != true) { return Task.CompletedTask; } httpContext.Response.OnCompleted( () => { //TODO Refactor to avoid method with too many parameters (maybe a CallbackRunnerFactory?) var fireAndForgetTask = PerformCallbacks(httpContext, mockacoContext, scriptContext, templateTransformer, options.Value); return Task.CompletedTask; }); return Task.CompletedTask; } private async Task PerformCallbacks( HttpContext httpContext, IMockacoContext mockacoContext, IScriptContext scriptContext, ITemplateTransformer templateTransformer, MockacoOptions options) { try { var stopwatch = Stopwatch.StartNew(); var template = await templateTransformer.Transform(mockacoContext.Mock.RawTemplate, scriptContext); var callbackTasks = new List(); foreach (var callbackTemplate in template.Callbacks) { callbackTasks.Add(PerformCallback(httpContext, callbackTemplate, options, stopwatch.ElapsedMilliseconds)); } await Task.WhenAll(callbackTasks); } catch (Exception ex) { _logger.LogError(ex, "Error preparing callback(s)"); } } private async Task PerformCallback(HttpContext httpContext, CallbackTemplate callbackTemplate, MockacoOptions options, long elapsedMilliseconds) { try { var request = PrepareHttpRequest(callbackTemplate, options); var httpClient = PrepareHttpClient(httpContext, callbackTemplate); await DelayRequest(callbackTemplate, elapsedMilliseconds); var stopwatch = Stopwatch.StartNew(); _logger.LogDebug("Callback started"); await PerformRequest(request, httpClient); _logger.LogDebug("Callback finished in {0} ms", stopwatch.ElapsedMilliseconds); } catch (Exception ex) { _logger.LogError(ex, "Callback error"); } } private static HttpRequestMessage PrepareHttpRequest(CallbackTemplate callbackTemplate, MockacoOptions options) { var request = new HttpRequestMessage(new HttpMethod(callbackTemplate.Method), callbackTemplate.Url); var formatting = callbackTemplate.Indented.GetValueOrDefault(true) ? Formatting.Indented : default; if (callbackTemplate.Body != null) { request.Content = callbackTemplate.Headers?.ContainsKey(HttpHeaders.ContentType) == true ? new StringContent(callbackTemplate.Body.ToString(), Encoding.UTF8, callbackTemplate.Headers[HttpHeaders.ContentType]) : new StringContent(callbackTemplate.Body.ToString(formatting)); } PrepareHeaders(callbackTemplate, request, options); return request; } private static void PrepareHeaders(CallbackTemplate callBackTemplate, HttpRequestMessage httpRequest, MockacoOptions options) { if (callBackTemplate.Headers != null) { foreach (var header in callBackTemplate.Headers.Where(h => h.Key != HttpHeaders.ContentType)) { if (httpRequest.Headers.Contains(header.Key)) { httpRequest.Headers.Remove(header.Key); } httpRequest.Headers.Add(header.Key, header.Value); } } if (!httpRequest.Headers.Accept.Any()) { httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(options.DefaultHttpContentType)); } } private static HttpClient PrepareHttpClient(HttpContext httpContext, CallbackTemplate callbackTemplate) { var factory = httpContext.RequestServices.GetService(); var httpClient = factory.CreateClient(); httpClient.Timeout = TimeSpan.FromMilliseconds(callbackTemplate.Timeout.GetValueOrDefault()); return httpClient; } private async Task DelayRequest(CallbackTemplate callbackTemplate, long elapsedMilliseconds) { var remainingDelay = TimeSpan.FromMilliseconds(callbackTemplate.Delay.GetValueOrDefault() - elapsedMilliseconds); if (elapsedMilliseconds < remainingDelay.TotalMilliseconds) { _logger.LogDebug("Callback delay: {0} milliseconds", remainingDelay.TotalMilliseconds); await Task.Delay(remainingDelay); } } private async Task PerformRequest(HttpRequestMessage request, HttpClient httpClient) { try { var response = await httpClient.SendAsync(request); _logger.LogDebug("Callback response\n\n{0}\n", response); _logger.LogDebug("Callback response content\n\n{0}\n", await response.Content.ReadAsStringAsync()); } catch (OperationCanceledException) { _logger.LogError("Callback request timeout"); } catch (Exception ex) { _logger.LogError(ex, "Callback error"); } } } } ================================================ FILE: src/Mockaco.AspNetCore/Middlewares/ChaosMiddleware.cs ================================================ using System.Net; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Mockaco.Chaos.Strategies; namespace Mockaco; internal class ChaosMiddleware { private readonly RequestDelegate _next; private readonly IEnumerable _strategies; private readonly ILogger _logger; private readonly IOptions _options; private List ErrorList { get; set; } private int Counter { get; set; } public ChaosMiddleware( RequestDelegate next, IEnumerable strategies, ILogger logger, IOptions options) { _next = next; _strategies = strategies; _logger = logger; _options = options; } public async Task Invoke(HttpContext httpContext) { if (!_options.Value.Enabled) { await _next(httpContext); return; } Counter++; if (Counter > 100) Counter = 1; if (Counter == 1) ErrorList = GenerateErrorList(_options.Value.ChaosRate); if (ErrorList.Contains(Counter)) { var selected = _strategies.Random(); _logger.LogInformation($"Chaos: {selected?.ToString()}"); if (selected != null) await selected.Response(httpContext.Response); } if (httpContext.Response.StatusCode != (int)HttpStatusCode.OK) return; await _next(httpContext); } private List GenerateErrorList(int rate) { var list = new List(); while (list.Count < rate) { var item = new Random().Next(1, 100); if (!list.Contains(item)) { list.Add(item); } } return list.OrderBy(x => x).ToList(); } } ================================================ FILE: src/Mockaco.AspNetCore/Middlewares/ErrorHandlingMiddleware.cs ================================================ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Linq; using System.Threading.Tasks; namespace Mockaco { internal class ErrorHandlingMiddleware { private readonly RequestDelegate _next; public ErrorHandlingMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke( HttpContext httpContext, IMockacoContext mockacoContext, IOptionsSnapshot statusCodeOptions, IMockProvider mockProvider, ILogger logger) { try { await _next(httpContext); } catch (Exception ex) { logger.LogError(ex, "Error generating mocked response"); mockacoContext.Errors.Add(new Error("Error generating mocked response", ex)); } finally { if (mockacoContext.Errors.Any() && !httpContext.Response.HasStarted) { httpContext.Response.StatusCode = (int)statusCodeOptions.Value.ErrorHttpStatusCode; httpContext.Response.ContentType = HttpContentTypes.ApplicationJson; IncludeMockProviderErrors(mockacoContext, mockProvider); await httpContext.Response.WriteAsync(mockacoContext.Errors.ToJson()); } } } private static void IncludeMockProviderErrors(IMockacoContext mockacoContext, IMockProvider mockProvider) { mockacoContext.Errors .AddRange(mockProvider.GetErrors() .Select(_ => new Error($"{_.TemplateName} - {_.ErrorMessage}"))); } } } ================================================ FILE: src/Mockaco.AspNetCore/Middlewares/RequestMatchingMiddleware.cs ================================================ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; namespace Mockaco { internal class RequestMatchingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public RequestMatchingMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task Invoke( HttpContext httpContext, IMockacoContext mockacoContext, IScriptContext scriptContext, IMockProvider mockProvider, ITemplateTransformer templateTransformer, IEnumerable requestMatchers, IMemoryCache cache, IOptions options ) { await LogHttpContext(httpContext); await AttachRequestToScriptContext(httpContext, mockacoContext, scriptContext); if (mockacoContext.Errors.Any()) { return; } foreach (var mock in mockProvider.GetMocks()) { if (await requestMatchers.AllAsync(_ => _.IsMatch(httpContext.Request, mock))) { cache.Set($"{nameof(RequestMatchingMiddleware)} {httpContext.Request.Path.Value}",new { Route = httpContext.Request.Path.Value, Timestamp = $"{DateTime.Now.ToString("t")}", Headers = LoadHeaders(httpContext, options.Value.VerificationIgnoredHeaders), Body = await httpContext.Request.ReadBodyStream() }, DateTime.Now.AddMinutes(options.Value.MatchedRoutesCacheDuration)); _logger.LogInformation("Incoming request matched {mock}", mock); await scriptContext.AttachRouteParameters(httpContext.Request, mock); var template = await templateTransformer.TransformAndSetVariables(mock.RawTemplate, scriptContext); mockacoContext.Mock = mock; mockacoContext.TransformedTemplate = template; await _next(httpContext); return; } else { _logger.LogDebug("Incoming request didn't match {mock}", mock); } } _logger.LogInformation("Incoming request didn't match any mock"); mockacoContext.Errors.Add(new Error("Incoming request didn't match any mock")); } //TODO Remove redundant code private async Task AttachRequestToScriptContext(HttpContext httpContext, IMockacoContext mockacoContext, IScriptContext scriptContext) { try { await scriptContext.AttachRequest(httpContext.Request); } catch (Exception ex) { mockacoContext.Errors.Add(new Error("An error occurred while reading request", ex)); _logger.LogWarning(ex, "An error occurred while reading request"); return; } } private async Task LogHttpContext(HttpContext httpContext) { _logger.LogInformation("Incoming request from {remoteIp}", httpContext.Connection.RemoteIpAddress); _logger.LogDebug("Headers: {headers}", httpContext.Request.Headers.ToJson()); var body = await httpContext.Request.ReadBodyStream(); if (string.IsNullOrEmpty(body)) { _logger.LogDebug("Body is not present"); } else { _logger.LogDebug("Body: {body}", body); } } private static IEnumerable LoadHeaders(HttpContext httpContext, IEnumerable verificationIgnoredHeaders) { return from header in httpContext.Request.Headers.ToList() where !verificationIgnoredHeaders.Any(opt => opt == header.Key) select new { header.Key, Value = header.Value[0] }; } } } ================================================ FILE: src/Mockaco.AspNetCore/Middlewares/ResponseDelayMiddleware.cs ================================================ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System.Diagnostics; using System.Threading.Tasks; namespace Mockaco { internal class ResponseDelayMiddleware { private readonly RequestDelegate _next; public ResponseDelayMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext, IMockacoContext mockacoContext, ILogger logger) { var transformedTemplate = mockacoContext.TransformedTemplate; if (transformedTemplate == default) { return; } int responseDelay = transformedTemplate.Response?.Delay.GetValueOrDefault() ?? 0; if (responseDelay > 0) { logger.LogDebug("Response delay: {responseDelay} milliseconds", responseDelay); await Task.Delay(responseDelay); } await _next(httpContext); } } } ================================================ FILE: src/Mockaco.AspNetCore/Middlewares/ResponseMockingMiddleware.cs ================================================ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using System.Net; using System.Threading.Tasks; namespace Mockaco { internal class ResponseMockingMiddleware { private readonly RequestDelegate _next; public ResponseMockingMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke( HttpContext httpContext, IMockacoContext mockacoContext, IScriptContext scriptContext, IResponseBodyFactory responseBodyFactory, IOptionsSnapshot options) { await PrepareResponse(httpContext.Response, mockacoContext.TransformedTemplate, responseBodyFactory, options.Value); await scriptContext.AttachResponse(httpContext.Response, mockacoContext.TransformedTemplate.Response); await _next(httpContext); } private async Task PrepareResponse( HttpResponse httpResponse, Template transformedTemplate, IResponseBodyFactory responseBodyFactory, MockacoOptions options) { httpResponse.StatusCode = GetResponseStatusFromTemplate(transformedTemplate.Response, options); AddHeadersFromTemplate(httpResponse, transformedTemplate.Response, options); var bodyBytes = await responseBodyFactory.GetResponseBodyBytesFromTemplate(transformedTemplate.Response); if (bodyBytes == default) { return; } await httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length, default); } private int GetResponseStatusFromTemplate(ResponseTemplate responseTemplate, MockacoOptions options) { return responseTemplate?.Status == default(HttpStatusCode) ? (int)options.DefaultHttpStatusCode : (int)responseTemplate.Status; } private void AddHeadersFromTemplate(HttpResponse response, ResponseTemplate responseTemplate, MockacoOptions options) { if (responseTemplate?.Headers != null) { foreach (var header in responseTemplate.Headers) { response.Headers.Add(header.Key, header.Value); } } if (string.IsNullOrEmpty(response.ContentType)) { response.ContentType = options.DefaultHttpContentType; } } } } ================================================ FILE: src/Mockaco.AspNetCore/MockProvider.cs ================================================ using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging; using Mockaco.HealthChecks; using Mono.TextTemplating; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; namespace Mockaco { internal class MockProvider : IMockProvider { private List _cache; private readonly List<(string TemplateName, string ErrorMessage)> _errors = new List<(string TemplateName, string Error)>(); private readonly IFakerFactory _fakerFactory; private readonly IRequestBodyFactory _requestBodyFactory; private readonly ITemplateProvider _templateProvider; private readonly ITemplateTransformer _templateTransformer; private readonly IGlobalVariableStorage _globalVariableStorage; private readonly StartupHealthCheck _healthCheck; private readonly ILogger _logger; public MockProvider (IFakerFactory fakerFactory, IRequestBodyFactory requestBodyFactory, ITemplateProvider templateProvider, ITemplateTransformer templateTransformer, IGlobalVariableStorage globalVariableStorage, StartupHealthCheck healthCheck, ILogger logger) { _cache = new List(); _fakerFactory = fakerFactory; _requestBodyFactory = requestBodyFactory; _templateProvider = templateProvider; _templateProvider.OnChange += TemplateProviderChange; _templateTransformer = templateTransformer; _globalVariableStorage = globalVariableStorage; _healthCheck = healthCheck; _logger = logger; } private async void TemplateProviderChange(object sender, EventArgs e) { await WarmUp(); } //TODO: Fix potential thread-unsafe method public List GetMocks() { return _cache; } public IEnumerable<(string TemplateName, string ErrorMessage)> GetErrors() { return _errors; } public async Task WarmUp() { var stopwatch = Stopwatch.StartNew(); var warmUpScriptContext = new ScriptContext(_fakerFactory, _requestBodyFactory, _globalVariableStorage); const int defaultCapacity = 16; var mocks = new List(_cache.Count > 0 ? _cache.Count : defaultCapacity); _errors.Clear(); foreach (var rawTemplate in _templateProvider.GetTemplates()) { try { var existentCachedRoute = _cache.FirstOrDefault(cachedRoute => cachedRoute.RawTemplate.Hash == rawTemplate.Hash); if (existentCachedRoute != default) { _logger.LogDebug("Using cached {0} ({1})", rawTemplate.Name, rawTemplate.Hash); mocks.Add(existentCachedRoute); continue; } _logger.LogDebug("Loading {0} ({1})", rawTemplate.Name, rawTemplate.Hash); var template = await _templateTransformer.Transform(rawTemplate, warmUpScriptContext); var mock = CreateMock(rawTemplate, template.Request); mocks.Add(mock); _logger.LogInformation("{method} {route} mapped from {templateName}", template.Request?.Method, template.Request?.Route, rawTemplate.Name); } catch (JsonReaderException ex) { _errors.Add((rawTemplate.Name, $"Generated JSON is invalid - {ex.Message}")); _logger.LogWarning("Skipping {0}: Generated JSON is invalid - {1}", rawTemplate.Name, ex.Message); } catch (ParserException ex) { _errors.Add((rawTemplate.Name, $"Script parser error - {ex.Message} {ex.Location}")); _logger.LogWarning("Skipping {0}: Script parser error - {1} {2} ", rawTemplate.Name, ex.Message, ex.Location); } catch (Exception ex) { _errors.Add((rawTemplate.Name, ex.Message)); _logger.LogWarning("Skipping {0}: {1}", rawTemplate.Name, ex.Message); } } _cache.Clear(); _cache = mocks.OrderByDescending(r => r.HasCondition).ToList(); _healthCheck.StartupCompleted = true; _logger.LogTrace("{0} finished in {1} ms", nameof(WarmUp), stopwatch.ElapsedMilliseconds); } private static Mock CreateMock(IRawTemplate rawTemplate, RequestTemplate requestTemplate) { return new Mock(requestTemplate?.Method, requestTemplate?.Route, rawTemplate, requestTemplate?.Condition != default); } } } ================================================ FILE: src/Mockaco.AspNetCore/Mockaco.AspNetCore.csproj ================================================  net6.0 latest Mockaco.AspNetCore Mockaco enable Debug;Release;Nuget true true ./nupkg false natenho ASP.NET Core pipeline to mock HTTP requests/responses, useful to stub services and simulate dynamic API responses, leveraging ASP.NET Core features, built-in fake data generation and pure C# scripting https://github.com/natenho/Mockaco https://github.com/natenho/Mockaco mock http aspnetcore Apache-2.0 mockaco-icon.png Mockaco 1701;1702;NU5104 AD0001;NU5104 1701;1702;NU5104 AD0001;NU5104 1701;1702;NU5104 AD0001;NU5104 all runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive <_Parameter1>Mockaco.Tests True / ================================================ FILE: src/Mockaco.AspNetCore/MockacoContext.cs ================================================ using System.Collections.Generic; namespace Mockaco { internal class MockacoContext : IMockacoContext { public IScriptContext ScriptContext { get; set; } public Template TransformedTemplate { get; set; } public Mock Mock { get; set; } public List Errors { get; set; } public MockacoContext(IScriptContext scriptContext) { ScriptContext = scriptContext; Errors = new List(); } } } ================================================ FILE: src/Mockaco.AspNetCore/Options/ChaosOptions.cs ================================================ namespace Mockaco; public class ChaosOptions { public bool Enabled { get; set; } public int ChaosRate { get; set; } public int MinimumLatencyTime { get; set; } public int MaximumLatencyTime { get; set; } public int TimeBeforeTimeout { get; set; } public ChaosOptions() { Enabled = false; ChaosRate = 10; MinimumLatencyTime = 500; MaximumLatencyTime = 3000; TimeBeforeTimeout = 10000; } } ================================================ FILE: src/Mockaco.AspNetCore/Options/MockacoOptions.cs ================================================ using System.Net; namespace Mockaco { public class MockacoOptions { public HttpStatusCode DefaultHttpStatusCode { get; set; } public HttpStatusCode ErrorHttpStatusCode { get; set; } public string DefaultHttpContentType { get; set; } public List References { get; set; } public List VerificationIgnoredHeaders { get; set; } public List Imports { get; set; } public int MatchedRoutesCacheDuration { get; set; } public string MockacoEndpoint { get; set; } // Deprecated (use MockacoEndpoint instead) public string VerificationEndpointPrefix { get; set; } public ChaosOptions Chaos { get; set; } public string VerificationEndpointName { get; set; } public TemplateFileProviderOptions TemplateFileProvider { get; set; } public MockacoOptions() { DefaultHttpStatusCode = HttpStatusCode.OK; ErrorHttpStatusCode = HttpStatusCode.NotImplemented; DefaultHttpContentType = HttpContentTypes.ApplicationJson; References = new List(); Imports = new List(); MatchedRoutesCacheDuration = 60; MockacoEndpoint = "_mockaco"; VerificationEndpointName = "verification"; TemplateFileProvider = new(); Chaos = new ChaosOptions(); } } } ================================================ FILE: src/Mockaco.AspNetCore/Options/TemplateFileProviderOptions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Mockaco { public class TemplateFileProviderOptions { public string Path { get; set; } public TemplateFileProviderOptions() { Path = "Mocks"; } } } ================================================ FILE: src/Mockaco.AspNetCore/Plugins/PhoneNumberExtensions.cs ================================================ using Bogus.DataSets; namespace Bogus { // TODO Move to plugin public static class PhoneNumberExtensions { public static string BrazilianPhoneNumber(this PhoneNumbers phoneNumbers) { var faker = new Faker("pt_BR"); string[] brazilianAreaCodes = { "11", "12", "13", "14", "15", "16", "17", "18", "19", "21", "22", "24", "27", "28", "31", "32", "33", "34", "35", "37", "38", "41", "42", "43", "44", "45", "46", "47", "48", "49", "51", "53", "54", "55", "61", "62", "63", "64", "65", "66", "67", "68", "69", "71", "73", "74", "75", "77", "79", "81", "82", "83", "84", "85", "86", "87", "88", "89", "91", "92", "93", "94", "95", "96", "97", "98", "99" }; var areaCode = faker.PickRandom(brazilianAreaCodes); var sufix = faker.Random.Number(50000000, 99999999); return $"{areaCode}9{sufix}"; } } } ================================================ FILE: src/Mockaco.AspNetCore/PublicAPI.Shipped.txt ================================================  ================================================ FILE: src/Mockaco.AspNetCore/PublicAPI.Unshipped.txt ================================================ Bogus.PhoneNumberExtensions Microsoft.AspNetCore.Builder.MockacoApplicationBuilder Microsoft.Extensions.DependencyInjection.MockacoServiceCollection Mockaco.ChaosOptions Mockaco.ChaosOptions.ChaosOptions() -> void Mockaco.ChaosOptions.ChaosRate.get -> int Mockaco.ChaosOptions.ChaosRate.set -> void Mockaco.ChaosOptions.Enabled.get -> bool Mockaco.ChaosOptions.Enabled.set -> void Mockaco.ChaosOptions.MaximumLatencyTime.get -> int Mockaco.ChaosOptions.MaximumLatencyTime.set -> void Mockaco.ChaosOptions.MinimumLatencyTime.get -> int Mockaco.ChaosOptions.MinimumLatencyTime.set -> void Mockaco.ChaosOptions.TimeBeforeTimeout.get -> int Mockaco.ChaosOptions.TimeBeforeTimeout.set -> void Mockaco.HealthChecks.StartupHealthCheck Mockaco.HealthChecks.StartupHealthCheck.CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task Mockaco.HealthChecks.StartupHealthCheck.StartupCompleted.get -> bool Mockaco.HealthChecks.StartupHealthCheck.StartupCompleted.set -> void Mockaco.HealthChecks.StartupHealthCheck.StartupHealthCheck() -> void Mockaco.IFakerFactory Mockaco.IFakerFactory.GetDefaultFaker() -> Bogus.Faker Mockaco.IFakerFactory.GetFaker(System.Collections.Generic.IEnumerable acceptLanguages) -> Bogus.Faker Mockaco.IGlobalVariableStorage Mockaco.IGlobalVariableStorage.Clear() -> void Mockaco.IGlobalVariableStorage.DisableWriting() -> void Mockaco.IGlobalVariableStorage.EnableWriting() -> void Mockaco.IGlobalVariableStorage.this[string name].get -> object Mockaco.IGlobalVariableStorage.this[string name].set -> void Mockaco.IRawTemplate Mockaco.IRawTemplate.Content.get -> string Mockaco.IRawTemplate.Hash.get -> string Mockaco.IRawTemplate.Name.get -> string Mockaco.IRequestBodyFactory Mockaco.IRequestBodyFactory.ReadBodyAsJson(Microsoft.AspNetCore.Http.HttpRequest httpRequest) -> System.Threading.Tasks.Task Mockaco.IScriptContext Mockaco.IScriptContext.AttachRequest(Microsoft.AspNetCore.Http.HttpRequest httpRequest) -> System.Threading.Tasks.Task Mockaco.IScriptContext.AttachResponse(Microsoft.AspNetCore.Http.HttpResponse httpResponse, Mockaco.ResponseTemplate responseTemplate) -> System.Threading.Tasks.Task Mockaco.IScriptContext.AttachRouteParameters(Microsoft.AspNetCore.Http.HttpRequest httpRequest, Mockaco.Mock route) -> System.Threading.Tasks.Task Mockaco.IScriptContext.Faker.get -> Bogus.Faker Mockaco.IScriptContext.Global.get -> Mockaco.IGlobalVariableStorage Mockaco.IScriptContext.Request.get -> Mockaco.ScriptContextRequest Mockaco.IScriptContext.Request.set -> void Mockaco.IScriptContext.Response.get -> Mockaco.ScriptContextResponse Mockaco.IScriptContext.Response.set -> void Mockaco.Mock Mockaco.Mock.HasCondition.get -> bool Mockaco.Mock.HasCondition.set -> void Mockaco.Mock.Method.get -> string Mockaco.Mock.Method.set -> void Mockaco.Mock.Mock(string method, string route, Mockaco.IRawTemplate rawTemplate, bool hasCondition) -> void Mockaco.Mock.RawTemplate.get -> Mockaco.IRawTemplate Mockaco.Mock.RawTemplate.set -> void Mockaco.Mock.Route.get -> string Mockaco.Mock.Route.set -> void Mockaco.MockacoOptions Mockaco.MockacoOptions.Chaos.get -> Mockaco.ChaosOptions Mockaco.MockacoOptions.Chaos.set -> void Mockaco.MockacoOptions.DefaultHttpContentType.get -> string Mockaco.MockacoOptions.DefaultHttpContentType.set -> void Mockaco.MockacoOptions.DefaultHttpStatusCode.get -> System.Net.HttpStatusCode Mockaco.MockacoOptions.DefaultHttpStatusCode.set -> void Mockaco.MockacoOptions.ErrorHttpStatusCode.get -> System.Net.HttpStatusCode Mockaco.MockacoOptions.ErrorHttpStatusCode.set -> void Mockaco.MockacoOptions.VerificationIgnoredHeaders.get -> System.Collections.Generic.List Mockaco.MockacoOptions.VerificationIgnoredHeaders.set -> void Mockaco.MockacoOptions.Imports.get -> System.Collections.Generic.List Mockaco.MockacoOptions.Imports.set -> void Mockaco.MockacoOptions.MatchedRoutesCacheDuration.get -> int Mockaco.MockacoOptions.MatchedRoutesCacheDuration.set -> void Mockaco.MockacoOptions.MockacoEndpoint.get -> string Mockaco.MockacoOptions.MockacoEndpoint.set -> void Mockaco.MockacoOptions.MockacoOptions() -> void Mockaco.MockacoOptions.References.get -> System.Collections.Generic.List Mockaco.MockacoOptions.References.set -> void Mockaco.MockacoOptions.TemplateFileProvider.get -> Mockaco.TemplateFileProviderOptions Mockaco.MockacoOptions.TemplateFileProvider.set -> void Mockaco.MockacoOptions.VerificationEndpointName.get -> string Mockaco.MockacoOptions.VerificationEndpointName.set -> void Mockaco.MockacoOptions.VerificationEndpointPrefix.get -> string Mockaco.MockacoOptions.VerificationEndpointPrefix.set -> void Mockaco.ResponseTemplate Mockaco.ResponseTemplate.Body.get -> Newtonsoft.Json.Linq.JToken Mockaco.ResponseTemplate.Body.set -> void Mockaco.ResponseTemplate.Delay.get -> int? Mockaco.ResponseTemplate.Delay.set -> void Mockaco.ResponseTemplate.File.get -> string Mockaco.ResponseTemplate.File.set -> void Mockaco.ResponseTemplate.Headers.get -> System.Collections.Generic.IDictionary Mockaco.ResponseTemplate.Headers.set -> void Mockaco.ResponseTemplate.Indented.get -> bool? Mockaco.ResponseTemplate.Indented.set -> void Mockaco.ResponseTemplate.ResponseTemplate() -> void Mockaco.ResponseTemplate.Status.get -> System.Net.HttpStatusCode Mockaco.ResponseTemplate.Status.set -> void Mockaco.ScriptContext Mockaco.ScriptContext.AttachRequest(Microsoft.AspNetCore.Http.HttpRequest httpRequest) -> System.Threading.Tasks.Task Mockaco.ScriptContext.AttachResponse(Microsoft.AspNetCore.Http.HttpResponse httpResponse, Mockaco.ResponseTemplate responseTemplate) -> System.Threading.Tasks.Task Mockaco.ScriptContext.AttachRouteParameters(Microsoft.AspNetCore.Http.HttpRequest httpRequest, Mockaco.Mock mock) -> System.Threading.Tasks.Task Mockaco.ScriptContext.Faker.get -> Bogus.Faker Mockaco.ScriptContext.Global.get -> Mockaco.IGlobalVariableStorage Mockaco.ScriptContext.Request.get -> Mockaco.ScriptContextRequest Mockaco.ScriptContext.Request.set -> void Mockaco.ScriptContext.Response.get -> Mockaco.ScriptContextResponse Mockaco.ScriptContext.Response.set -> void Mockaco.ScriptContext.ScriptContext(Mockaco.IFakerFactory fakerFactory, Mockaco.IRequestBodyFactory requestBodyFactory, Mockaco.IGlobalVariableStorage globalVarialeStorage) -> void Mockaco.ScriptContextRequest Mockaco.ScriptContextRequest.Body.get -> Newtonsoft.Json.Linq.JToken Mockaco.ScriptContextRequest.Header.get -> System.Collections.Generic.IReadOnlyDictionary Mockaco.ScriptContextRequest.Query.get -> System.Collections.Generic.IReadOnlyDictionary Mockaco.ScriptContextRequest.Route.get -> System.Collections.Generic.IReadOnlyDictionary Mockaco.ScriptContextRequest.ScriptContextRequest(System.Uri url, System.Collections.Generic.IReadOnlyDictionary route, System.Collections.Generic.IReadOnlyDictionary query, System.Collections.Generic.IReadOnlyDictionary header, Newtonsoft.Json.Linq.JToken body) -> void Mockaco.ScriptContextRequest.Url.get -> System.Uri Mockaco.ScriptContextResponse Mockaco.ScriptContextResponse.Body.get -> Newtonsoft.Json.Linq.JToken Mockaco.ScriptContextResponse.Header.get -> System.Collections.Generic.IReadOnlyDictionary Mockaco.ScriptContextResponse.ScriptContextResponse(Mockaco.StringDictionary header, Newtonsoft.Json.Linq.JToken body) -> void Mockaco.StringDictionary Mockaco.StringDictionary.Add(string key, string value) -> void Mockaco.StringDictionary.Replace(string key, string value) -> void Mockaco.StringDictionary.StringDictionary() -> void Mockaco.StringDictionary.this[string key].get -> string Mockaco.StringDictionary.this[string key].set -> void Mockaco.TemplateFileProviderOptions Mockaco.TemplateFileProviderOptions.Path.get -> string Mockaco.TemplateFileProviderOptions.Path.set -> void Mockaco.TemplateFileProviderOptions.TemplateFileProviderOptions() -> void override Mockaco.Mock.ToString() -> string static Bogus.PhoneNumberExtensions.BrazilianPhoneNumber(this Bogus.DataSets.PhoneNumbers phoneNumbers) -> string static Microsoft.AspNetCore.Builder.MockacoApplicationBuilder.UseMockaco(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.AspNetCore.Builder.MockacoApplicationBuilder.UseMockaco(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, System.Action configure) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.Extensions.DependencyInjection.MockacoServiceCollection.AddMockaco(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection static Microsoft.Extensions.DependencyInjection.MockacoServiceCollection.AddMockaco(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.Configuration.IConfiguration config) -> Microsoft.Extensions.DependencyInjection.IServiceCollection static Microsoft.Extensions.DependencyInjection.MockacoServiceCollection.AddMockaco(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action config) -> Microsoft.Extensions.DependencyInjection.IServiceCollection ================================================ FILE: src/Mockaco.AspNetCore/Settings/VerificationRouteValueTransformer.cs ================================================ namespace Mockaco.Settings { using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; internal class VerificationRouteValueTransformer : DynamicRouteValueTransformer { public override ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) { values["controller"] = "verify"; values["action"] = "Get"; return ValueTask.FromResult(values); } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Cli/GeneratorRunner.cs ================================================ using System; using System.CommandLine; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace Mockaco.Templating.Generating.Cli { internal class GeneratorRunner { private readonly IServiceProvider _serviceProvider; public GeneratorRunner(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task ExecuteAsync(GeneratingOptions options, IConsole console, CancellationToken cancellationToken) { using var scope = _serviceProvider.CreateScope(); await scope.ServiceProvider.GetRequiredService().GenerateAsync(options, cancellationToken); return 0; } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/GeneratedTemplate.cs ================================================ namespace Mockaco.Templating.Generating { internal class GeneratedTemplate : Template { public string Name { get; set; } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/GeneratingOptions.cs ================================================ using System; using System.IO; namespace Mockaco.Templating.Generating { internal class GeneratingOptions { public string Source { get; set; } public Uri SourceUri { get { if (Uri.TryCreate(Source, UriKind.Absolute, out Uri uri)) { return uri; } else if (Path.IsPathFullyQualified(Source)) { return new Uri(new Uri(Uri.UriSchemeFile), Source); } else { throw new ArgumentException("Provided Source cannot be converted to Uri"); } } } public string Provider { get; set; } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Providers/GeneratedTemplateProviderFactory.cs ================================================ using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; namespace Mockaco.Templating.Generating.Providers { internal class GeneratedTemplateProviderFactory : IGeneratedTemplateProviderFactory { private readonly IServiceProvider _serviceProvider; private readonly IDictionary _providersRegistry = new Dictionary { {"openapi", typeof(OpenApiTemplateProvider)} }; public GeneratedTemplateProviderFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IGeneratedTemplateProvider Create(string providerType) { if (_providersRegistry.ContainsKey(providerType)) { return (IGeneratedTemplateProvider)_serviceProvider.GetRequiredService(_providersRegistry[providerType]); } throw new NotSupportedException($"Provider {providerType} is not supported."); } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Providers/IGeneratedTemplateProvider.cs ================================================ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; namespace Mockaco.Templating.Generating.Providers { internal interface IGeneratedTemplateProvider { Task> GetTemplatesAsync(Stream sourceStream, CancellationToken cancellationToken = default); } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Providers/IGeneratedTemplateProviderFactory.cs ================================================ namespace Mockaco.Templating.Generating.Providers { internal interface IGeneratedTemplateProviderFactory { IGeneratedTemplateProvider Create(string providerType); } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Providers/OpenApiTemplateProvider.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers; using Newtonsoft.Json.Linq; namespace Mockaco.Templating.Generating.Providers { internal class OpenApiTemplateProvider : IGeneratedTemplateProvider { private readonly ILogger _logger; public OpenApiTemplateProvider(ILogger logger) { _logger = logger; } public Task> GetTemplatesAsync(Stream sourceStream, CancellationToken cancellationToken = default) { var openApiDocument = new OpenApiStreamReader( new OpenApiReaderSettings { ReferenceResolution = ReferenceResolutionSetting.ResolveLocalReferences } ) .Read(sourceStream, out var diagnostic); LogDiagnostics(diagnostic); return Task.FromResult(BuildTemplates(openApiDocument)); } private IEnumerable BuildTemplates(OpenApiDocument openApiDocument) { foreach (var path in openApiDocument.Paths) { foreach (var operation in path.Value.Operations) { var template = new GeneratedTemplate { Request = BuildRequest(path, operation), Response = BuildResponse(operation) }; SetTemplateName(template, operation); yield return template; } } } private void SetTemplateName(GeneratedTemplate template, KeyValuePair operation) { template.Name = operation.Value.OperationId; if (string.IsNullOrWhiteSpace(template.Name)) { template.Name = Guid.NewGuid().ToString("N"); _logger.LogWarning( "The OperationId for request {Method} {Route} hasn't been declared. Using generated template name: {TemplateName}", template.Request.Method, template.Request.Route, template.Name); } } private ResponseTemplate BuildResponse(KeyValuePair operation) { var responseTemplate = new ResponseTemplate(); if (operation.Value.Responses.Any()) { var response = operation.Value.Responses.First(); responseTemplate.Status = (HttpStatusCode) int.Parse(response.Key); if (response.Value.Content.Any()) { var content = response.Value.Content.First(); responseTemplate.Headers.Add("Content-Type", content.Key); if (content.Value.Example != null) { responseTemplate.Body = JValue.CreateString(content.Value.Example.ToString()); } } } else { responseTemplate.Status = HttpStatusCode.NotFound; } return responseTemplate; } private RequestTemplate BuildRequest(KeyValuePair path, KeyValuePair operation) { return new RequestTemplate { Route = path.Key, Method = Enum.GetName(operation.Key)?.ToUpper() }; } private void LogDiagnostics(OpenApiDiagnostic diagnostic) { _logger.LogDebug("OpenApi specification version: {OpenApiSpecVersion}", Enum.GetName(diagnostic.SpecificationVersion)); foreach (var diagnosticError in diagnostic.Errors) { _logger.LogWarning("OpenApi parser diagnostic error: {Error}", diagnosticError); } } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/ServiceCollectionExtensions.cs ================================================ using Mockaco.Templating.Generating; using Mockaco.Templating.Generating.Cli; using Mockaco.Templating.Generating.Providers; using Mockaco.Templating.Generating.Source; using Mockaco.Templating.Generating.Store; using System.CommandLine; using System.CommandLine.Invocation; namespace Microsoft.Extensions.DependencyInjection { internal static class ServiceCollectionExtensions { public static IServiceCollection AddTemplatesGenerating(this IServiceCollection services) { services .AddTransient() .AddTransient() .AddTransient() .AddTransient() .AddTransient(); services.AddOptions() .Configure(options => { options.RootDir = "Mocks"; }) .BindConfiguration("Mockaco:TemplateFileProvider:Path"); services.AddTransient(); services.AddTransient(provider => { var cmd = new Command("generate") { new Argument("source"), new Option(new[] { "--provider"}) { IsRequired = true }, new Option("--path") }; cmd.Handler = CommandHandler.Create( provider.GetRequiredService().ExecuteAsync); return cmd; }); return services; } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Source/HttpContentProvider.cs ================================================ using System; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace Mockaco.Templating.Generating.Source { internal class HttpContentProvider : ISourceContentProvider { public async Task GetStreamAsync(Uri sourceUri, CancellationToken cancellationToken) { return await new HttpClient().GetStreamAsync(sourceUri, cancellationToken); } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Source/ISourceContentProvider.cs ================================================ using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace Mockaco.Templating.Generating.Source { internal interface ISourceContentProvider { Task GetStreamAsync(Uri sourceUri, CancellationToken cancellationToken); } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Source/LocalFileContentProvider.cs ================================================ using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace Mockaco.Templating.Generating.Source { internal class LocalFileContentProvider : ISourceContentProvider { public Task GetStreamAsync(Uri sourceUri, CancellationToken cancellationToken) { return Task.FromResult((Stream)File.OpenRead(sourceUri.LocalPath)); } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Source/SourceContentProviderComposite.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace Mockaco.Templating.Generating.Source { internal class SourceContentProviderComposite : ISourceContentProvider { private readonly IServiceProvider _serviceProvider; private readonly IDictionary, Type> _providersRegistry = new Dictionary, Type>(); public SourceContentProviderComposite(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; RegisterDefaultProviders(); } private void RegisterDefaultProviders() { Register(uri => uri.Scheme.Equals(Uri.UriSchemeFile) || Path.IsPathFullyQualified(uri.OriginalString), typeof(LocalFileContentProvider)); Register(uri => uri.Scheme.Equals(Uri.UriSchemeHttp) || uri.Scheme.Equals(Uri.UriSchemeHttps), typeof(HttpContentProvider)); } private ISourceContentProvider Create(Uri sourceUri) { foreach (var registryItem in _providersRegistry) { var canHandle = registryItem.Key; var providerType = registryItem.Value; if (canHandle(sourceUri)) { return (ISourceContentProvider)Activator.CreateInstance(registryItem.Value); } } throw new NotSupportedException("Specified URI is not supported"); } public void Register(Predicate canHandle, Type type) { _providersRegistry.Add(canHandle, type); } public Task GetStreamAsync(Uri sourceUri, CancellationToken cancellationToken) { var specificProvider = Create(sourceUri); return specificProvider.GetStreamAsync(sourceUri, cancellationToken); } } } ================================================ FILE: src/Mockaco.AspNetCore/Templating/Generating/Store/GeneratedTemplateStore.cs ================================================ using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Mockaco.Templating.Generating.Store { internal class GeneratedTemplateStore : IGeneratedTemplateStore { private const string FileExtension = ".json"; private readonly IOptions _options; private readonly ILogger _logger; public GeneratedTemplateStore(IOptions options, ILogger logger) { _options = options; _logger = logger; } private string RootDir => _options.Value.RootDir; public async Task SaveAsync(GeneratedTemplate template, CancellationToken cancellationToken = default) { var templateFileName = GetFileName(template); Directory.CreateDirectory(Path.GetDirectoryName(templateFileName) ?? string.Empty); await File.WriteAllBytesAsync(templateFileName, GetTemplateContent(template), cancellationToken); _logger.LogInformation("Generated {template} for {method} {route}", templateFileName, template.Request.Method, template.Request.Route); } private byte[] GetTemplateContent(GeneratedTemplate template) { return JsonSerializer.SerializeToUtf8Bytes