{"id":21730151,"url":"https://github.com/nventive/reviewservice","last_synced_at":"2026-02-20T22:39:24.049Z","repository":{"id":177016599,"uuid":"653715489","full_name":"nventive/ReviewService","owner":"nventive","description":"A multi-platform recipe to rate mobile apps.","archived":false,"fork":false,"pushed_at":"2026-01-20T15:04:10.000Z","size":12350,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-25T21:22:35.448Z","etag":null,"topics":["android","dotnet","ios","maui","mobile","uno-platform"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nventive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-06-14T15:22:50.000Z","updated_at":"2026-01-20T15:04:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"eb760ee4-545d-4341-b318-f9cbd45e5031","html_url":"https://github.com/nventive/ReviewService","commit_stats":null,"previous_names":["nventive/reviewservice"],"tags_count":1,"template":false,"template_full_name":"nventive/Template","purl":"pkg:github/nventive/ReviewService","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FReviewService","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FReviewService/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FReviewService/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FReviewService/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nventive","download_url":"https://codeload.github.com/nventive/ReviewService/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FReviewService/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29667093,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T19:49:36.704Z","status":"ssl_error","status_checked_at":"2026-02-20T19:44:05.372Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","dotnet","ios","maui","mobile","uno-platform"],"created_at":"2024-11-26T04:13:11.818Z","updated_at":"2026-02-20T22:39:24.040Z","avatar_url":"https://github.com/nventive.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# Review Service\n\nThis repository introduces abstractions around native review capabilities to ease code sharing and testability.\nIt also introduces business logic to quickly configure conditions and state tracking to prompt for reviews at the right moment.\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) ![Version](https://img.shields.io/nuget/v/ReviewService.Abstractions?style=flat-square) ![Downloads](https://img.shields.io/nuget/dt/ReviewService.Abstractions?style=flat-square)\n\n## Before Getting Started\n\nBefore getting started, please read the [Android](https://developer.android.com/guide/playcore/in-app-review) and [iOS](https://developer.apple.com/design/human-interface-guidelines/ratings-and-reviews) application review documentation.\n\n## Getting Started\n\n1. Add the `ReviewService` and `ReviewSerivce.NativePrompters` NuGet packages to your projects (Windows, Android and iOS).\n   \u003e 💡 If you need to implement more platforms or create custom implementations, you can use the `ReviewService.Abstractions` NuGet package.\n\n2. Create an instance of `ReviewService`. We'll cover dependency injection in details later on in this documentation.\n   ``` cs\n   using ReviewService;\n\n   var reviewConditionsBuilder = ReviewConditionsBuilder.Empty()\n      .MinimumPrimaryActionsCompleted(1);\n\n   var reviewService = new ReviewService\u003cReviewSettings\u003e(\n       logger: null,\n       reviewPrompter: new ReviewPrompter(logger: null),\n       reviewSettingsSource: new MemoryReviewSettingsSource(),\n       reviewConditionsBuilder: reviewConditionsBuilder\n   )\n   ```\n\n3. Use the service.\n   - Update the review settings based on application events.\n      ``` cs\n      using ReviewService;\n\n      private readonly IReviewService\u003cReviewSettings\u003e _reviewService;\n\n      public async Task DoPrimaryAction(CancellationToken ct)\n      {\n          // Do Primary Action.\n\n          // Track this action.\n          await _reviewService.TrackPrimaryActionCompleted(ct)\n      }\n      ```\n   - Use the service to request review.\n      ``` cs\n      using ReviewService;\n\n      private readonly IReviewService\u003cReviewSettings\u003e _reviewService;\n\n      public async Task OnCompletedImportantFlow(CancellationToken ct)\n      {\n          // Do Meaningful Task.\n\n          // Check if all conditions are satisfied and prompt for review if they are.\n          var result = await _reviewService.TryRequestReview(ct);\n    \n          if (result.IsSuccessful)\n          {\n              // Review prompt was successfully shown\n          }\n          else\n          {\n              Console.WriteLine( $ \"Review request status: {result.Status}.\");\n          }\n      }\n      ```\n\n   \u003cimg src=\"docs/review_prompt_android.gif\" alt=\"Review Prompt Android\" width=\"250\"\u003e\n   \u003cimg src=\"docs/review_prompt_ios.gif\" alt=\"Review Prompt iOS\" width=\"250\"\u003e\n\n## Next Steps\n\n### Persisting Review Settings\n\n`MemoryReviewSettingsSource` is great for automated testing but should not be the implementation of choice for real use-cases. Instead, you should create your own implementation that persists data on the device (so that review settings don't reset when you kill the app).\n\n``` cs\nusing ReviewService;\n\n/// \u003csummary\u003e\n/// Storage implementation of \u003csee cref=\"IReviewSettingsSource{TReviewSettings}\"/\u003e.\n/// \u003c/summary\u003e\n/// \u003ctypeparam name=\"TReviewSettings\"\u003eThe type of the persisted object.\u003c/typeparam\u003e\npublic sealed class StorageReviewSettingsSource\u003cTReviewSettings\u003e : IReviewSettingsSource\u003cTReviewSettings\u003e\n    where TReviewSettings : ReviewSettings\n{\n    /// \u003cinheritdoc/\u003e\n    public Task\u003cTReviewSettings\u003e Read(CancellationToken ct)\n    {\n        // TODO: Return stored review settings.\n    }\n\n    /// \u003cinheritdoc/\u003e\n    public Task Write(CancellationToken ct, TReviewSettings reviewSettings)\n    {\n        // TODO: Update stored review settings.\n    }\n}\n```\n\n### Using Dependency Injection\n\nHere is a simple code that does dependency injection using `Microsoft.Extensions.DependencyInjection` and `Microsoft.Extensions.Hosting`.\n\n``` cs\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing ReviewService;\n\nvar host = new HostBuilder()\n    .ConfigureServices(serviceCollection =\u003e serviceCollection\n        .AddSingleton\u003cIReviewPrompter, ReviewPrompter\u003e()\n        .AddSingleton\u003cIReviewSettingsSource\u003cReviewSettingsCustom\u003e, ReviewSettingsSource\u003e()\n        .AddTransient(s =\u003e ReviewConditionsBuilder\n            .Default\u003cReviewSettingsCustom\u003e()\n        )\n        .AddSingleton\u003cIReviewService\u003cReviewSettingsCustom\u003e, ReviewService\u003cReviewSettingsCustom\u003e\u003e()\n    )\n    .Build();\n```\n\n\u003e 💡 We recommend that you define your own interface that wraps `IReviewService\u003cYouChoiceOfReviewSettings\u003e` to make the usage code leaner and ease any potential refactorings.\n\u003e ```csharp\n\u003e /// \u003csummary\u003e\n\u003e /// This interface wraps \u003csee cref=\"IReviewService{TReviewSettings}\"/\u003e so that you don't have to repeat the generic parameter everywhere that you would use the review service.\n\u003e /// In other words, you should use this interface in the app instead of \u003csee cref=\"IReviewService{TReviewSettings}\"/\u003e because it's leaner.\n\u003e /// \u003c/summary\u003e\n\u003e /// \u003cremarks\u003e\n\u003e /// If you would change \u003csee cref=\"ReviewSettings\"/\u003e for a custom type, using this interface allows you to minimize any refactoring effort by limiting it to this interface and the associated adapter.\n\u003e /// \u003c/remarks\u003e\n\u003e public interface IReviewService : IReviewService\u003cReviewSettings\u003e\n\u003e {\n\u003e }\n\u003e ```\n\u003e Here's a full example.\n\u003e ```csharp\n\u003e public static class ReviewConfiguration\n\u003e {\n\u003e \tpublic static IServiceCollection AddReviewServices(this IServiceCollection services)\n\u003e \t{\n\u003e \t\treturn services\n\u003e \t\t\t.AddTransient(s =\u003e ReviewConditionsBuilder\n\u003e \t\t\t\t.Empty()\n\u003e \t\t\t\t.MinimumPrimaryActionsCompleted(3)\n\u003e \t\t\t)\n\u003e \t\t\t.AddSingleton\u003cIReviewPrompter, LoggingReviewPrompter\u003e()\n\u003e \t\t\t.AddSingleton\u003cIReviewSettingsSource\u003cReviewSettings\u003e, MemoryReviewSettingsSource\u003cReviewSettings\u003e\u003e()\n\u003e \t\t\t.AddSingleton\u003cIReviewService\u003cReviewSettings\u003e, ReviewService\u003cReviewSettings\u003e\u003e()\n\u003e \t\t\t.AddSingleton\u003cIReviewService, ReviewServiceAdapter\u003e();\n\u003e \t}\n\u003e \n\u003e \tprivate sealed class ReviewServiceAdapter : IReviewService\n\u003e \t{\n\u003e \t\tprivate readonly IReviewService\u003cReviewSettings\u003e _reviewService;\n\u003e \n\u003e \t\tpublic ReviewServiceAdapter(IReviewService\u003cReviewSettings\u003e reviewService)\n\u003e \t\t{\n\u003e \t\t\t_reviewService = reviewService;\n\u003e \t\t}\n\u003e \n\u003e \t\tpublic Task\u003cbool\u003e GetAreConditionsSatisfied(CancellationToken ct) =\u003e _reviewService.GetAreConditionsSatisfied(ct);\n\u003e \n\u003e \t\tpublic async Task\u003cReviewRequestResult\u003e TryRequestReview(CancellationToken ct)\n\u003e \t\t{\n\u003e \t\t\tvar status = await _reviewService.TryRequestReview(ct);\n\u003e \t\t\treturn new ReviewRequestResult(status);\n\u003e \t\t} \n\u003e \n\u003e \t\tpublic Task UpdateReviewSettings(CancellationToken ct, Func\u003cReviewSettings, ReviewSettings\u003e updateFunction) =\u003e _reviewService.UpdateReviewSettings(ct, updateFunction);\n\u003e \t}\n\u003e }\n\u003e ```\n\n## Features\n\nNow that everything is setup, Let's see what else we can do!\n\n### Tack Application Events\n\nTo track the provided review settings you can use the following `IReviewService` extensions.\n\u003e 💡 The review request count and the last review request are automatically tracked by the service.\n\n- [TrackApplicationLaunched](https://github.com/nventive/ReviewService/blob/a78e37c7e9b6fbe07ba1fec5d0f2b3b2f31bf356/src/ReviewService.Abstractions/ReviewService.Extensions.cs#L20) : Tracks that the application was launched (Also tracks if it's the first launch).\n- [TrackPrimaryActionCompleted](https://github.com/nventive/ReviewService/blob/a78e37c7e9b6fbe07ba1fec5d0f2b3b2f31bf356/src/ReviewService.Abstractions/ReviewService.Extensions.cs#L45) : Tracks that a primary action was completed.\n- [TrackSecondaryActionCompleted](https://github.com/nventive/ReviewService/blob/a78e37c7e9b6fbe07ba1fec5d0f2b3b2f31bf356/src/ReviewService.Abstractions/ReviewService.Extensions.cs#L61) : Tracks that a secondary action was completed.\n\n### Customize Tracking Data\n\nIf you need custom conditions for your application, you have to create another record that inherits from `ReviewSettings`.\n\n ``` cs\nusing ReviewService;\n\n/// \u003csummary\u003e\n/// The custom review prompt settings used for prompt conditions.\n/// \u003c/summary\u003e\npublic record ReviewSettingsCustom : ReviewSettings\n{\n    /// \u003csummary\u003e\n    /// Gets or sets if the application onboarding has been completed.\n    /// \u003c/summary\u003e\n    public bool HasCompletedOnboarding { get; init; }\n}\n```\n\n### Add Tracking for Custom Application Events\n\nTo track your custom review settings, you can create extensions for `IReviewService` and be sure to make them generic so they are usable with custom review settings.\n\n``` cs\nusing ReviewService;\n\n/// \u003csummary\u003e\n/// Extensions of \u003csee cref=\"IReviewService{TReviewSettings}\"/\u003e.\n/// \u003c/summary\u003e\npublic static class ReviewServiceExtensions\n{\n    /// \u003csummary\u003e\n    /// Tracks that the application onboarding has been completed.\n    /// \u003c/summary\u003e\n    /// \u003ctypeparam name=\"TReviewSettings\"\u003eThe type of the object that we use for tracking.\u003c/typeparam\u003e\n    /// \u003cparam name=\"reviewService\"\u003e\u003csee cref=\"IReviewService{TReviewSettings}\"/\u003e.\u003c/param\u003e\n    /// \u003cparam name=\"ct\"\u003eThe cancellation token.\u003c/param\u003e\n    /// \u003creturns\u003e\u003csee cref=\"Task\"/\u003e.\u003c/returns\u003e\n    public static async Task TrackOnboardingCompleted\u003cTReviewSettings\u003e(this IReviewService\u003cTReviewSettings\u003e reviewService, CancellationToken ct)\n        where TReviewSettings : ReviewSettingsCustom\n    {\n        await reviewService.UpdateReviewSettings(ct, reviewSettings =\u003e\n        {\n            return reviewSettings with { HasCompletedOnboarding = reviewSettings.HasCompletedOnboarding };\n        });\n    }\n}\n```\n\n#### Built-in Tracking Data\n\n- [PrimaryActionCompletedCount](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewSettings.cs#L13) : The number of primary actions completed.\n- [SecondaryActionCompletedCount](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewSettings.cs#L18) : The number of secondary actions completed.\n- [ApplicationLaunchCount](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewSettings.cs#L23) : The number of times the application has been launched.\n- [FirstApplicationLaunch](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewSettings.cs#L28) : When the application first started.\n- [RequestCount](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewSettings.cs#L33) : The number of review requested.\n- [LastRequest](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewSettings.cs#L38) : When the last review was requested.\n\n### Configure Conditions\n\nIf you want to use our default review conditions, you can use `ReviewConditionsBuilder.Default()` and pass it to the `ReviewService` constructor, or register it as a transient dependency when using dependency injection. Please note that our review conditions are also generic, so they can be used with custom review settings too.\n\nThe `ReviewConditionsBuilder.Default()` extension method uses the following conditions.\n- **3** application launches required.\n- **2** completed primary actions.\n- **5** days since the first application launch.\n- **15** days since the last review request.\n\n#### Built-in Conditions\n\n- [MinimumPrimaryActionsCompleted](https://github.com/nventive/ReviewService/blob/a78e37c7e9b6fbe07ba1fec5d0f2b3b2f31bf356/src/ReviewService.Abstractions/ReviewConditionsBuilder.Extensions.cs#L17) : Make sure that it prompts for review only if the number of completed primary actions meets the minimum.\n- [MinimumSecondaryActionsCompleted](https://github.com/nventive/ReviewService/blob/a78e37c7e9b6fbe07ba1fec5d0f2b3b2f31bf356/src/ReviewService.Abstractions/ReviewConditionsBuilder.Extensions.cs#L33) : Make sure that it prompts for review only if the number of completed secondary actions meets the minimum.\n- [MinimumApplicationLaunchCount](https://github.com/nventive/ReviewService/blob/a78e37c7e9b6fbe07ba1fec5d0f2b3b2f31bf356/src/ReviewService.Abstractions/ReviewConditionsBuilder.Extensions.cs#L49) : Make sure that it prompts for review only if the number of times the application has been launched meets the required minimum.\n- [MinimumElapsedTimeSinceApplicationFirstLaunch](https://github.com/nventive/ReviewService/blob/a78e37c7e9b6fbe07ba1fec5d0f2b3b2f31bf356/src/ReviewService.Abstractions/ReviewConditionsBuilder.Extensions.cs#L65) : Make sure that it prompts for review only if the elapsed time since the first application launch meets the required minimum.\n- [MinimumElapsedTimeSinceLastReviewRequest](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewConditionsBuilder.Extensions.cs#L83) : Make sure that it prompts for review only if the elapsed time since the last review request meets the required minimum.\n- [Custom](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewConditionsBuilder.Extensions.cs#L99) : Custom condition made with a synchronous lambda function.\n- [CustomAsync](https://github.com/nventive/ReviewService/blob/1484f946c60e2bf1cb86b27faa60c148a1e56d45/src/ReviewService.Abstractions/ReviewConditionsBuilder.Extensions.cs#L113) : Custom asynchronous condition made with an asynchronous lambda function.\n\n### Add Custom Conditions\n\nTo create custom review conditions, you have to use `ReviewConditionsBuilder.Custom` and `ReviewConditionsBuilder.CustomAsync` and provide them with a function directly instead of a condition. Also you can create extensions for `IReviewConditionsBuilder` and add a new condition to the builder. To create a review condition, you can use both `SynchronousReviewCondition` and `AsynchronousReviewCondition` you need to provide them with a function.\n\n``` cs\nnamespace ReviewService;\n\n/// \u003csummary\u003e\n/// Extensions for \u003csee cref=\"IReviewConditionsBuilder{TReviewSettings}\"/\u003e.\n/// \u003c/summary\u003e\npublic static partial class ReviewConditionsBuilderExtensions\n{\n    /// \u003csummary\u003e\n    /// The application onboarding must be completed.\n    /// \u003c/summary\u003e\n    /// \u003ctypeparam name=\"TReviewSettings\"\u003eThe type of the object that we use for tracking.\u003c/typeparam\u003e\n    /// \u003cparam name=\"builder\"\u003eThe builder.\u003c/param\u003e\n    /// \u003creturns\u003e\u003csee cref=\"IReviewConditionsBuilder{TReviewSettings}\"/\u003e.\u003c/returns\u003e\n    public static IReviewConditionsBuilder\u003cTReviewSettings\u003e ApplicationOnboardingCompleted\u003cTReviewSettings\u003e(this IReviewConditionsBuilder\u003cTReviewSettings\u003e builder)\n        where TReviewSettings : ReviewSettingsCustom\n    {\n        builder.Conditions.Add(new SynchronousReviewCondition\u003cTReviewSettings\u003e(\n            (reviewSettings, currentDateTime) =\u003e reviewSettings.HasCompletedOnboarding is true)\n        );\n        return builder;\n    }\n}\n```\n\nHere is a simple code that uses the builder extensions for review conditions.\n\n ``` cs\nvar reviewConditionsBuilder = ReviewConditionsBuilder.Empty()\n    .MinimumPrimaryActionsCompleted(1)\n    .MinimumSecondaryActionsCompleted(1)\n    .MinimumApplicationLaunchCount(1)\n    .MinimumTimeElapsedSinceApplicationFirstLaunch(TimeSpan.FromDays(1))\n    .Custom((reviewSettings, currentDateTime) =\u003e\n    {\n        return reviewSettings.PrimaryActionCompletedCount + reviewSettings.SecondaryActionCompletedCount \u003e= 2;\n    });\n```\n\nIt's possible to customize the review conditions used by the service by using `ReviewConditionsBuilder` and passing it to the `ReviewService` constructor or by injecting it as a transient when using dependency injection.\n\n``` cs\nusing ReviewService;\n\nvar reviewConditionsBuilder = ReviewConditionsBuilder.Empty\u003cReviewSettingsCustom\u003e()\n    .ApplicationOnboardingCompleted()\n    .MinimumPrimaryActionsCompleted(3)\n    .MinimumApplicationLaunchCount(3)\n    .MinimumTimeElapsedSinceApplicationFirstLaunch(TimeSpan.FromDays(5))\n    .Custom((reviewSettings, currentDateTime) =\u003e\n    {\n        return reviewSettings.PrimaryActionCompletedCount + reviewSettings.SecondaryActionCompletedCount \u003e= 2;\n    });\n```\n\n## Testing\n\nThis is what you need to know before testing and debugging this service. Please note that this may change and you should always refer to the [Apple](https://developer.apple.com/documentation/storekit/skstorereviewcontroller/3566727-requestreview#4278434) and [Android](https://developer.android.com/guide/playcore/in-app-review/test) documentation for the most up-to-date information.\n\n### Android\n\n- You can't test this service while debugging the application, the prompt won't show up. To test it, you need to use the [internal application sharing](https://play.google.com/console/about/internalappsharing/) or the internal testing feature in Google Play Console. See [this](https://developer.android.com/guide/playcore/in-app-review/test) for more details.\n- You can't use a Google Suite account on Google Play to review an application because the prompt will not show up.\n\n### iOS\n\n- You can test on a real device or on a simulator.\n- You can test this service only while debugging the application (It won't show up on TestFlight).\n\n## Acknowledgements\n\nTake a look at [StoreReviewPlugin](https://github.com/jamesmontemagno/StoreReviewPlugin) that we use to prompt for review.\n\n## Breaking Changes\n\nPlease consult [BREAKING_CHANGES.md](BREAKING_CHANGES.md) for more information about version\nhistory and compatibility.\n\n## License\n\nThis project is licensed under the Apache 2.0 license - see the\n[LICENSE](LICENSE) file for details.\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the process for\ncontributing to this project.\n\nBe mindful of our [Code of Conduct](CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnventive%2Freviewservice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnventive%2Freviewservice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnventive%2Freviewservice/lists"}