diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..9a3b5210127 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,58 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +name: RELEASE + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + artifacts: + name: Release + runs-on: ubuntu-latest +# needs: [unittests, integrationtests] + steps: + - uses: actions/checkout@v2 + # Authenticates packages to push to GPR + - uses: actions/setup-dotnet@v1 + with: + dotnet-version: '3.1' + source-url: https://nuget.pkg.github.com/commercetools/index.json + env: + NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Retrieve branch name + id: branch_name + run: echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/} + - run: ./patch_csproj.rb --version $SOURCE_TAG --patch **/*.csproj + env: + SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }} + - run: dotnet restore --configfile Nuget.config --packages ../packages + working-directory: ./commercetools.Sdk + - run: dotnet build --no-restore --source ../packages -c Release + working-directory: ./commercetools.Sdk + - name: Create the package + run: dotnet pack -c Release -o ../pack/ --configfile Nuget.config + working-directory: ./commercetools.Sdk + - name: List packages + run: ls -la pack + - name: Publish Nuget to GitHub registry + run: dotnet nuget push "pack/*.nupkg" -k ${GITHUB_TOKEN} -s https://nuget.pkg.github.com/commercetools/index.json --skip-duplicate --no-symbols true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/upload-artifact@v1 + with: + name: commercetools.Sdkv2.${{ steps.branch_name.outputs.SOURCE_TAG }} + path: pack + - name: Push generated package to NuGet + run: dotnet nuget push "pack/*.nupkg" --api-key ${{ secrets.NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols true + - name: Create GH Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: false + prerelease: false diff --git a/README.md b/README.md new file mode 100644 index 00000000000..af5c44b7201 --- /dev/null +++ b/README.md @@ -0,0 +1,142 @@ +# commercetools .NET SDK + +## Introduction + +This repository contains the commercetools platform C# SDK generated from our api reference. The SDK enables developers to communicate with the [commercetools HTTP API](https://docs.commercetools.com/http-api.html). The developers do not need to create plain HTTP requests, but they can instead use the domain specific classes and methods to formulate valid requests. + +## Technical Overview + +The SDK consists of the following projects: +* `commercetools.Base.Abstractions`: Contains common classes and interfaces that can be used in other SDK projects like IClient and ISerializerService. +* `commercetools.Base.Client`: Contains CtpClient which communicate with the platform to execute requests, it contains also the classes related to the client like tokens,middlewares and handlers. +* `commercetools.Base.Registration`: Helper classes for things like types retriever. +* `commercetools.Base.Serialization`: Serialization and deserialization services for responses and requests to the HTTP API using System.Text.Json. +* `commercetools.SDK.Api`: Contains all generated models and request builders to communicate with the platform api. + +In addition, the SDK has the following directories: +* `/IntegrationTests`: Integration tests for the SDK. A good way for anyone using the .NET SDK to understand it further. +* `/Tests`: Unit Tests for serialization and request builders. + + +## Getting Started with the .NET SDK + +All operations (get, query, create, update, delete and others) are available in the generated request builders, so you can build the request and use the client to execute it. all the request builders accessible through ApiRoot, you can use the instance inside the injected client using function client.WithApi() or use ApiFactory to create a new instance, In order to use the client object, it needs to be setup first through dependency injection setup. + +### Basic Workflow + +At a high level, to make a basic call to the API, do the following: + +1. Use the dependency injection class to set things up. +2. get a client object from the services responsible for calling requests to the API +3. use the ApiRoot instance inside the client and identify the project-key. +4. If needed – Create a draft object as a required for the request based on the documentation. +5. Build your request and execute it using ExecuteAsync. +6. Receive the response as a model. + +### Dependency Injection Setup + + In the ConfigureServices method of Startup.cs add the following: + +```c# +services.UseCommercetoolsApi( + this.configuration); // replace with your instance of IConfiguration +``` +### Configuration +The client configuration needs to be added to appsettings.json in order for the client to work. The structure is as follows: + +```c# +{ + "Client": { + "ClientId": "", // replace with your client ID + "ClientSecret": "", // replace with your client secret + "AuthorizationBaseAddress": "https://auth.europe-west1.gcp.commercetools.com/", // replace if needed + "Scope": "", // replace with your scope + "ProjectKey": "", // replace with your project key + "ApiBaseAddress": "https://api.europe-west1.gcp.commercetools.com/" // replace if needed + } +} +``` + +## Using SDK + +SDK follows a builder pattern when creating requests. Category resource will be used to demonstrate how to use the SDK. This behaviour is the same for all resources. +The IClient interface can be used by injecting it and calling its ExecuteAsync method for different requests. +```c# +private readonly IClient client; +public CategoryController(IClient client) +{ + this.client = client; +} +public void CreatingRequests() +{ + // Create CategoryDraft using builder pattern + var categoryDraft = new CategoryDraft + { + Name = new LocalizedString {{"en", "Name"}}, + Slug = new LocalizedString {{"en", "Slug"}}, + Key = "Key" + }; + + // Use in the previous step configured client instance to send and receive a newly created Category + var category = await client.WithApi().WithProjectKey("project-key") + .Categories() + .Post(categoryDraft) + .ExecuteAsync(); + + // Get Category by id + var queriedCategory = await client.WithApi().WithProjectKey("project-key") + .Categories() + .WithId(category.Id) + .Get() + .ExecuteAsync(); + + // Get Category by key + var queriedCategory = await client.WithApi().WithProjectKey("project-key") + .Categories() + .WithKey(category.Key) + .Get() + .ExecuteAsync(); + + // Query Categories + var response = await client.WithApi().WithProjectKey("project-key") + .Categories() + .Get() + .WithWhere($"key = \"{category.Key}\"") + .ExecuteAsync(); + + // Delete Category by id + var deletedCategory = await client.WithApi().WithProjectKey("project-key") + .Categories() + .WithId(category.Id) + .Delete() + .WithVersion(category.version) + .ExecuteAsync(); + + // Update Category + var newName = "newName"; + var action = new CategoryChangeNameAction + { + Name = new LocalizedString {{"en", newName}} + }; + var update = new CategoryUpdate + { + Version = category.Version, + Actions = new List {action} + }; + + var updatedCategory = await client.WithApi().WithProjectKey("project-key") + .Categories() + .WithId(category.Id) + .Post(categoryUpdate) + .ExecuteAsync(); + + // Delete Category by key + var deletedCategory = await client.WithApi().WithProjectKey("project-key") + .Categories() + .WithKey(category.Key) + .Delete() + .WithVersion(category.Version) + .ExecuteAsync(); + } + +``` \ No newline at end of file diff --git a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesFixture.cs b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesFixture.cs index e2e6788f54d..13f9d0d2d83 100644 --- a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesFixture.cs +++ b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesFixture.cs @@ -46,7 +46,7 @@ public static CategoryDraft DefaultCategoryDraftWithParent(CategoryDraft draft, public static async Task CreateCategory(IClient client, CategoryDraft categoryDraft) { - return await client.ApiRoot().WithProjectKey(DefaultProjectKey) + return await client.WithApi().WithProjectKey(DefaultProjectKey) .Categories() .Post(categoryDraft) .ExecuteAsync(); @@ -56,7 +56,7 @@ public static async Task DeleteCategory(IClient client, Category category) { try { - await client.ApiRoot().WithProjectKey(DefaultProjectKey) + await client.WithApi().WithProjectKey(DefaultProjectKey) .Categories() .WithId(category.Id) .Delete() diff --git a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesIntegrationTests.cs b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesIntegrationTests.cs index 0df5c75dd9a..16180171876 100644 --- a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesIntegrationTests.cs +++ b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Categories/CategoriesIntegrationTests.cs @@ -46,7 +46,7 @@ await WithCategory( async category => { Assert.NotNull(category); - var retrievedCategory = await _client.ApiRoot().WithProjectKey(_projectKey) + var retrievedCategory = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .WithId(category.Id) .Get() @@ -66,7 +66,7 @@ await WithCategory( async category => { Assert.NotNull(category); - var retrievedCategory = await _client.ApiRoot().WithProjectKey(_projectKey) + var retrievedCategory = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .WithKey(category.Key) .Get() @@ -86,7 +86,7 @@ await WithCategory( async category => { Assert.NotNull(category); - var returnedSet = await _client.ApiRoot().WithProjectKey(_projectKey) + var returnedSet = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .Get() .WithWhere($"key = \"{category.Key}\"") @@ -107,7 +107,7 @@ await WithCategory( { Assert.NotNull(category); - var returnedSet = await _client.ApiRoot().WithProjectKey(_projectKey) + var returnedSet = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .Get() .WithWhere($"key = \"{category.Key}\"") @@ -140,7 +140,7 @@ await WithListOfCategories( var orderedCategoriesNames = categoriesList.OrderBy(c => c.Name["en"]).Select(c => c.Name["en"]).ToList(); - var returnedSet = await _client.ApiRoot().WithProjectKey(_projectKey) + var returnedSet = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .Get() .WithWhere($"parent(id = \"{parentCategory.Id}\")") @@ -172,7 +172,7 @@ await WithListOfCategories( var orderedCategoriesNames = categoriesList.OrderByDescending(c => c.Name["en"]).Select(c => c.Name["en"]).ToList(); - var returnedSet = await _client.ApiRoot().WithProjectKey(_projectKey) + var returnedSet = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .Get() .WithWhere($"parent(id = \"{parentCategory.Id}\")") @@ -205,7 +205,7 @@ await WithListOfCategories( await AssertEventuallyAsync(async () => { - var returnedSet = await _client.ApiRoot().WithProjectKey(_projectKey) + var returnedSet = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .Get() .WithWhere($"parent(id = \"{parentCategory.Id}\")") @@ -236,7 +236,7 @@ await WithCategory( { Assert.NotNull(category); - await _client.ApiRoot().WithProjectKey(_projectKey) + await _client.WithApi().WithProjectKey(_projectKey) .Categories() .WithId(category.Id) .Delete() @@ -244,7 +244,7 @@ await _client.ApiRoot().WithProjectKey(_projectKey) .ExecuteAsync(); await Assert.ThrowsAsync( - () => _client.ApiRoot().WithProjectKey(_projectKey).Categories().WithId(category.Id).Get().ExecuteAsync()); + () => _client.WithApi().WithProjectKey(_projectKey).Categories().WithId(category.Id).Get().ExecuteAsync()); }); } @@ -258,7 +258,7 @@ await WithCategory( { Assert.NotNull(category); - await _client.ApiRoot().WithProjectKey(_projectKey) + await _client.WithApi().WithProjectKey(_projectKey) .Categories() .WithKey(category.Key) .Delete() @@ -266,7 +266,7 @@ await _client.ApiRoot().WithProjectKey(_projectKey) .ExecuteAsync(); await Assert.ThrowsAsync( - () => _client.ApiRoot().WithProjectKey(_projectKey).Categories().WithId(category.Id).Get().ExecuteAsync()); + () => _client.WithApi().WithProjectKey(_projectKey).Categories().WithId(category.Id).Get().ExecuteAsync()); }); } @@ -279,7 +279,7 @@ await WithCategory( async category => { Assert.NotNull(category); - var retrievedCategory = await _client.ApiRoot().WithProjectKey(_projectKey) + var retrievedCategory = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .WithId(category.Id) .Get() @@ -312,7 +312,7 @@ await WithUpdateableCategory(_client, async category => Actions = new List {action} }; - var updatedCategory = await _client.ApiRoot().WithProjectKey(_projectKey) + var updatedCategory = await _client.WithApi().WithProjectKey(_projectKey) .Categories() .WithKey(category.Key) .Post(update) diff --git a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Payments/PaymentsFixture.cs b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Payments/PaymentsFixture.cs index 3e25fd9ecd3..65c71115b6b 100644 --- a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Payments/PaymentsFixture.cs +++ b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Payments/PaymentsFixture.cs @@ -32,7 +32,7 @@ public static PaymentDraft DefaultPaymentDraftWithTransaction(PaymentDraft draft public static async Task CreatePayment(IClient client, PaymentDraft paymentDraft) { - var resource = await client.ApiRoot().WithProjectKey(DefaultProjectKey) + var resource = await client.WithApi().WithProjectKey(DefaultProjectKey) .Payments() .Post(paymentDraft) .ExecuteAsync(); @@ -43,7 +43,7 @@ public static async Task DeletePayment(IClient client, Payment payment) { try { - await client.ApiRoot().WithProjectKey(DefaultProjectKey) + await client.WithApi().WithProjectKey(DefaultProjectKey) .Payments() .WithId(payment.Id) .Delete() diff --git a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductProjectionSearch/ProductProjectionSearchIntegrationTests.cs b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductProjectionSearch/ProductProjectionSearchIntegrationTests.cs index 69875b0cf07..ac351b60b35 100644 --- a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductProjectionSearch/ProductProjectionSearchIntegrationTests.cs +++ b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductProjectionSearch/ProductProjectionSearchIntegrationTests.cs @@ -56,7 +56,7 @@ public async Task SearchByFullLocale() await AssertEventuallyAsync(async () => { //Act - var searchResult = await _client.ApiRoot() + var searchResult = await _client.WithApi() .WithProjectKey(_projectKey) .ProductProjections() .Search() diff --git a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductTypes/ProductTypesFixture.cs b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductTypes/ProductTypesFixture.cs index 6e4741d09f6..3edfab92e65 100644 --- a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductTypes/ProductTypesFixture.cs +++ b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/ProductTypes/ProductTypesFixture.cs @@ -22,7 +22,7 @@ public static ProductTypeDraft DefaultProductTypeDraft(ProductTypeDraft productT public static async Task CreateProductType(IClient client, ProductTypeDraft productTypeDraft) { - return await client.ApiRoot().WithProjectKey(DefaultProjectKey) + return await client.WithApi().WithProjectKey(DefaultProjectKey) .ProductTypes() .Post(productTypeDraft) .ExecuteAsync(); @@ -32,7 +32,7 @@ public static async Task DeleteProductType(IClient client, ProductType productTy { try { - await client.ApiRoot().WithProjectKey(DefaultProjectKey) + await client.WithApi().WithProjectKey(DefaultProjectKey) .ProductTypes() .WithId(productType.Id) .Delete() @@ -51,7 +51,7 @@ public static async Task CreateOrRetrieveProductType(IClient client ProductType productType = null; try { - productType = await client.ApiRoot().WithProjectKey(projectKey) + productType = await client.WithApi().WithProjectKey(projectKey) .ProductTypes() .WithKey(productTypeDraft.Key) .Get() @@ -59,7 +59,7 @@ public static async Task CreateOrRetrieveProductType(IClient client } catch (NotFoundException) { - productType = await client.ApiRoot() + productType = await client.WithApi() .WithProjectKey(projectKey) .ProductTypes() .Post(productTypeDraft).ExecuteAsync(); diff --git a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsFixture.cs b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsFixture.cs index 71190b4665e..f9d8d8f0841 100644 --- a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsFixture.cs +++ b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsFixture.cs @@ -59,7 +59,7 @@ public static ProductDraft DefaultProductDraftWithMultipleVariants(ProductDraft public static async Task CreateProduct(IClient client, ProductDraft productDraft) { - var resource = await client.ApiRoot().WithProjectKey(DefaultProjectKey) + var resource = await client.WithApi().WithProjectKey(DefaultProjectKey) .Products() .Post(productDraft) .ExecuteAsync(); @@ -73,7 +73,7 @@ public static async Task DeleteProduct(IClient client, Product product) var version = product.Version; if (product.MasterData.Published) { - var updatedProduct = await client.ApiRoot().WithProjectKey(DefaultProjectKey) + var updatedProduct = await client.WithApi().WithProjectKey(DefaultProjectKey) .Products() .WithId(product.Id) .Post(new ProductUpdate @@ -86,7 +86,7 @@ public static async Task DeleteProduct(IClient client, Product product) .ExecuteAsync(); version = updatedProduct.Version; } - await client.ApiRoot().WithProjectKey(DefaultProjectKey) + await client.WithApi().WithProjectKey(DefaultProjectKey) .Products() .WithId(product.Id) .Delete() @@ -105,7 +105,7 @@ public static async Task CreateOrRetrieveProduct(IClient client, Produc Product product = null; try { - product = await client.ApiRoot().WithProjectKey(projectKey) + product = await client.WithApi().WithProjectKey(projectKey) .Products() .WithKey(productDraft.Key) .Get() @@ -113,7 +113,7 @@ public static async Task CreateOrRetrieveProduct(IClient client, Produc } catch (NotFoundException) { - product = await client.ApiRoot() + product = await client.WithApi() .WithProjectKey(projectKey) .Products() .Post(productDraft).ExecuteAsync(); diff --git a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsIntegrationTests.cs b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsIntegrationTests.cs index 129f9f827d3..9d9a9f7c56b 100644 --- a/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsIntegrationTests.cs +++ b/commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsIntegrationTests.cs @@ -39,7 +39,7 @@ await WithProduct(_client, var file = new FileStream(logoPath, FileMode.Open, FileAccess.Read); var updateProduct = await _client - .ApiRoot() + .WithApi() .WithProjectKey(_projectKey) .Products() .WithId(product.Id) diff --git a/commercetools.Sdk/Tests/commercetools.Api.Tests/RequestBuilderTests.cs b/commercetools.Sdk/Tests/commercetools.Api.Tests/RequestBuilderTests.cs index aaf9bd0b2ae..361b32b35f2 100644 --- a/commercetools.Sdk/Tests/commercetools.Api.Tests/RequestBuilderTests.cs +++ b/commercetools.Sdk/Tests/commercetools.Api.Tests/RequestBuilderTests.cs @@ -32,7 +32,7 @@ public void TestGetByIdRequest() var additionalParam = new KeyValuePair("withTotal", "false"); //act - var request = GetClient().ApiRoot() + var request = GetClient().WithApi() .WithProjectKey(projectKey) .Categories() .WithId(categoryId) @@ -63,7 +63,7 @@ public void TestQueryRequest() var where = $"name = \"food\""; //act - var request = GetClient().ApiRoot() + var request = GetClient().WithApi() .WithProjectKey(projectKey) .Categories() .Get() @@ -96,7 +96,7 @@ public void TestDeleteByIdRequest() var additionalParam = new KeyValuePair("withTotal", "false"); //act - var request = GetClient().ApiRoot() + var request = GetClient().WithApi() .WithProjectKey(projectKey) .Categories() .WithId(categoryId) @@ -140,7 +140,7 @@ public void TestUpdateByIdRequest() //act - var request = GetClient().ApiRoot() + var request = GetClient().WithApi() .WithProjectKey(projectKey) .Categories() .WithId(categoryId) @@ -174,7 +174,7 @@ public void TestCreateRequest() }; //act - var request = GetClient().ApiRoot() + var request = GetClient().WithApi() .WithProjectKey(projectKey) .Categories() .Post(categoryDraft) diff --git a/commercetools.Sdk/commercetools.Sdk.Api/Client/Extensions.cs b/commercetools.Sdk/commercetools.Sdk.Api/Client/Extensions.cs index 0aec96873a6..6e7d96dcbd3 100644 --- a/commercetools.Sdk/commercetools.Sdk.Api/Client/Extensions.cs +++ b/commercetools.Sdk/commercetools.Sdk.Api/Client/Extensions.cs @@ -4,7 +4,7 @@ namespace commercetools.Api.Client { public static class Extensions { - public static ApiRoot ApiRoot(this IClient client) + public static ApiRoot WithApi(this IClient client) { return ApiFactory.Create(client); } diff --git a/commercetools.Sdk/compat/commercetools.Sdk.V2Compat/commercetools.Sdk.V2Compat.csproj b/commercetools.Sdk/compat/commercetools.Sdk.V2Compat/commercetools.Sdk.V2Compat.csproj index b49a517a53a..eb3b5077aad 100644 --- a/commercetools.Sdk/compat/commercetools.Sdk.V2Compat/commercetools.Sdk.V2Compat.csproj +++ b/commercetools.Sdk/compat/commercetools.Sdk.V2Compat/commercetools.Sdk.V2Compat.csproj @@ -4,6 +4,7 @@ netstandard2.1 0.1.0 https://github.com/commercetools/commercetools-dotnet-core-sdk-v2 + false