From 624c1e2d677401bb840983aa93dcd1fb6ca3c2b8 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Thu, 20 Oct 2022 16:45:12 -0500 Subject: [PATCH 01/12] Add deprecation/renaming notice for FulfillmentService.CreateForFulfillmentAsync method --- .../Services/Fulfillment/FulfillmentService.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ShopifySharp/Services/Fulfillment/FulfillmentService.cs b/ShopifySharp/Services/Fulfillment/FulfillmentService.cs index adaa6aa4..207b5d18 100644 --- a/ShopifySharp/Services/Fulfillment/FulfillmentService.cs +++ b/ShopifySharp/Services/Fulfillment/FulfillmentService.cs @@ -67,14 +67,14 @@ public virtual async Task GetAsync(long orderId, long fulfillmentId { return await ExecuteGetAsync($"orders/{orderId}/fulfillments/{fulfillmentId}.json", "fulfillment", fields, cancellationToken); } - + /// /// Creates a fulfillment for one or many fulfillment orders. /// /// An object containing fulfillment order and tracking data. /// Cancellation Token /// The new . - public virtual async Task CreateForFulfillmentAsync(FulfillmentShipping fulfillment, CancellationToken cancellationToken = default) + public virtual async Task CreateAsync(FulfillmentShipping fulfillment, CancellationToken cancellationToken = default) { var req = PrepareRequest($"fulfillments.json"); var body = fulfillment.ToDictionary(); @@ -108,6 +108,18 @@ public virtual async Task UpdateTrackingAsync(long fulfillmentId, F return response.Result; } + /// + /// Creates a fulfillment for one or many fulfillment orders. + /// + /// An object containing fulfillment order and tracking data. + /// Cancellation Token + /// The new . + [Obsolete("This method has been renamed to CreateAsync and will be removed in a future release.")] + public virtual async Task CreateForFulfillmentAsync(FulfillmentShipping fulfillment, CancellationToken cancellationToken = default) + { + return await CreateAsync(fulfillment, cancellationToken); + } + /// /// Creates a new on the order. /// API Reference From af14afb8c5f5c4a3bfcbeec9cb7034906a9d4b1f Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 24 Oct 2022 15:15:07 -0500 Subject: [PATCH 02/12] Remove deprecated fulfillment methods/endpoints --- .../Fulfillment/FulfillmentService.cs | 90 ------------------- 1 file changed, 90 deletions(-) diff --git a/ShopifySharp/Services/Fulfillment/FulfillmentService.cs b/ShopifySharp/Services/Fulfillment/FulfillmentService.cs index e1fd81ee..f5f276b5 100644 --- a/ShopifySharp/Services/Fulfillment/FulfillmentService.cs +++ b/ShopifySharp/Services/Fulfillment/FulfillmentService.cs @@ -119,95 +119,5 @@ public virtual async Task CreateForFulfillmentAsync(FulfillmentShip { return await CreateAsync(fulfillment, cancellationToken); } - - /// - /// Creates a new on the order. - /// API Reference - /// - /// The order id to which the fulfillments belong. - /// A new . Id should be set to null. - /// Cancellation Token - /// The new . - [Obsolete("This method will be removed in API version 2022-07 and ShopifySharp v5.19.0. Use the FulfillmentOrderService instead.")] - public virtual async Task CreateAsync(long orderId, Fulfillment fulfillment, CancellationToken cancellationToken = default) - { - var req = PrepareRequest($"orders/{orderId}/fulfillments.json"); - var body = fulfillment.ToDictionary(); - - var content = new JsonContent(new - { - fulfillment = body - }); - - var response = await ExecuteRequestAsync(req, HttpMethod.Post, cancellationToken, content, "fulfillment"); - return response.Result; - } - - /// - /// Updates the given . - /// - /// The order id to which the fulfillments belong. - /// Id of the object being updated. - /// The to update. - /// Cancellation Token - /// The updated . - [Obsolete("This method will be removed in API version 2022-07 and ShopifySharp v5.19.0. Use the FulfillmentOrderService instead.")] - public virtual async Task UpdateAsync(long orderId, long fulfillmentId, Fulfillment fulfillment, CancellationToken cancellationToken = default) - { - var req = PrepareRequest($"orders/{orderId}/fulfillments/{fulfillmentId}.json"); - var body = fulfillment.ToDictionary(); - var content = new JsonContent(new - { - fulfillment = body - }); - - var response = await ExecuteRequestAsync(req, HttpMethod.Put, cancellationToken, content, "fulfillment"); - return response.Result; - } - - /// - /// Completes a pending fulfillment with the given id. - /// - /// The order id to which the fulfillments belong. - /// The fulfillment's id. - /// Cancellation Token - [Obsolete("This method will be removed in API version 2022-07 and ShopifySharp v5.19.0. Use the FulfillmentOrderService instead.")] - public virtual async Task CompleteAsync(long orderId, long fulfillmentId, CancellationToken cancellationToken = default) - { - var req = PrepareRequest($"orders/{orderId}/fulfillments/{fulfillmentId}/complete.json"); - - var response = await ExecuteRequestAsync(req, HttpMethod.Post, cancellationToken, rootElement: "fulfillment"); - return response.Result; - } - - /// - /// Cancels a pending fulfillment with the given id. - /// - /// The order id to which the fulfillments belong. - /// The fulfillment's id. - /// Cancellation Token - [Obsolete("This method will be removed in API version 2022-07 and ShopifySharp v5.19.0. Use the FulfillmentOrderService instead.")] - public virtual async Task CancelAsync(long orderId, long fulfillmentId, CancellationToken cancellationToken = default) - { - var req = PrepareRequest($"orders/{orderId}/fulfillments/{fulfillmentId}/cancel.json"); - - var response = await ExecuteRequestAsync(req, HttpMethod.Post, cancellationToken, rootElement: "fulfillment"); - return response.Result; - } - - /// - /// Opens a pending fulfillment with the given id. - /// - /// The order id to which the fulfillments belong. - /// The fulfillment's id. - /// Cancellation Token - [Obsolete("This method will be removed in API version 2022-07 and ShopifySharp v5.19.0. Use the FulfillmentOrderService instead.")] - public virtual async Task OpenAsync(long orderId, long fulfillmentId, CancellationToken cancellationToken = default) - { - var req = PrepareRequest($"orders/{orderId}/fulfillments/{fulfillmentId}/open.json"); - - var response = await ExecuteRequestAsync(req, HttpMethod.Post, cancellationToken, rootElement: "fulfillment"); - return response.Result; - } } } From 1ba112bac759099b1801b96e78233a42e84f52c8 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 24 Oct 2022 15:15:12 -0500 Subject: [PATCH 03/12] Fix FulfillmentService tests --- ShopifySharp.Tests/Fulfillment_Tests.cs | 197 ++++-------------------- 1 file changed, 29 insertions(+), 168 deletions(-) diff --git a/ShopifySharp.Tests/Fulfillment_Tests.cs b/ShopifySharp.Tests/Fulfillment_Tests.cs index 2ff27198..b3494fd1 100644 --- a/ShopifySharp.Tests/Fulfillment_Tests.cs +++ b/ShopifySharp.Tests/Fulfillment_Tests.cs @@ -86,7 +86,7 @@ public async Task Creates_Fulfillments() public async Task Creates_Fulfillments_With_Tracking_Number() { var order = await Fixture.CreateOrder(); - var created = await Fixture.Create(order.Id.Value, false); + var created = await Fixture.Create(order.Id.Value); Assert.NotNull(created); Assert.True(created.Id.HasValue); @@ -98,115 +98,17 @@ public async Task Creates_Fulfillments_With_Tracking_Number() Assert.NotEmpty(created.TrackingUrls); } - [Fact] - public async Task Creates_Fulfillments_With_Unrecognized_Tracking_Number() - { - // If you create a fulfillment without a tracking company, Shopify will try to parse the tracking number - // to figure out the shipping company. If it doesn't recognize it, the tracking numbers will still be - // populated -- and will still appear in the order dashboard -- but there will be no tracking URLs - // or tracking company. - - var order = await Fixture.CreateOrder(); - var created = await Fixture.Service.CreateAsync(order.Id.Value, new Fulfillment - { - TrackingNumbers = new []{ "shopify_wont_recognize_this" }, - TrackingUrl = null, - TrackingCompany = null, - NotifyCustomer = false, - LocationId = Fixture.LocationId - }); - - // Add created fulfillment to fixture's created list so it can clean them up after test run - Fixture.Created.Add(created); - - Assert.NotNull(created); - Assert.True(created.Id.HasValue); - Assert.Equal("success", created.Status); - Assert.NotEmpty(created.TrackingNumber); - Assert.NotEmpty(created.TrackingNumbers); - Assert.Null(created.TrackingCompany); - Assert.Null(created.TrackingUrl); - Assert.Empty(created.TrackingUrls); - } - - [Fact] - public async Task Creates_Fulfillments_With_Unrecognized_Tracking_Number_And_Url() - { - // If you create a fulfillment without a tracking company, Shopify will try to parse the tracking number - // to figure out the shipping company. If it doesn't recognize it, the tracking numbers will still be - // populated -- and will still appear in the order dashboard -- but there will be no tracking URLs - // or tracking company **unless you add a URL when creating the fulfillment**. - - var order = await Fixture.CreateOrder(); - var created = await Fixture.Service.CreateAsync(order.Id.Value, new Fulfillment - { - TrackingNumbers = new []{ "shopify_wont_recognize_this" }, - TrackingUrl = "https://example.com/my-custom-tracking-url/with-tracking-number", - TrackingCompany = null, - NotifyCustomer = false, - LocationId = Fixture.LocationId - }); - - // Add created fulfillment to fixture's created list so it can clean them up after test run - Fixture.Created.Add(created); - - Assert.NotNull(created); - Assert.True(created.Id.HasValue); - Assert.Equal("success", created.Status); - Assert.NotEmpty(created.TrackingNumber); - Assert.NotEmpty(created.TrackingNumbers); - Assert.Null(created.TrackingCompany); - Assert.Equal("https://example.com/my-custom-tracking-url/with-tracking-number", created.TrackingUrl); - Assert.NotEmpty(created.TrackingUrls); - } - - [Fact] - public async Task Creates_Fulfillments_With_Tracking_Numbers() - { - var order = await Fixture.CreateOrder(); - var created = await Fixture.Create(order.Id.Value, true); - - Assert.NotNull(created); - Assert.True(created.Id.HasValue); - Assert.Equal("success", created.Status); - Assert.True(created.TrackingNumbers.Count() > 1); - } - [Fact] public async Task Creates_Partial_Fulfillments() { var order = await Fixture.CreateOrder(); - var lineItem = order.LineItems.First(); - - // A partial fulfillment does not fulfill the entire line item quantity - lineItem.Quantity -= 1; - - var created = await Fixture.Create(order.Id.Value, false, new LineItem[] { lineItem }); + var created = await Fixture.Create(order.Id.Value, partialFulfillment: true); Assert.NotNull(created); Assert.True(created.Id.HasValue); Assert.Equal("success", created.Status); } - [Fact] - public async Task Updates_Fulfillments() - { - string company = "Auntie Dot's Shipping Company"; - var order = await Fixture.CreateOrder(); - var created = await Fixture.Create(order.Id.Value, false); - long id = created.Id.Value; - - created.TrackingCompany = company; - created.Id = null; - - var updated = await Fixture.Service.UpdateAsync(created.OrderId.Value, id, created); - - // Reset the id so the Fixture can properly delete this object. - created.Id = id; - - Assert.Equal(company, updated.TrackingCompany); - } - [Fact] public async Task Updates_Tracking_Fulfillments() { @@ -214,7 +116,7 @@ public async Task Updates_Tracking_Fulfillments() string trackingNum = "123456789"; string trackingUrl = "https://example.com/123456789"; var order = await Fixture.CreateOrder(); - var created = await Fixture.Create(order.Id.Value, false); + var created = await Fixture.Create(order.Id.Value); long id = created.Id.Value; var update = new FulfillmentShipping() @@ -238,43 +140,13 @@ public async Task Updates_Tracking_Fulfillments() Assert.Equal(trackingNum, updated.TrackingNumbers.First()); Assert.Equal(trackingUrl, updated.TrackingUrls.First()); } - - [Fact(Skip = "Can't complete/cancel/open a fulfillment whose status is not 'pending'. It's not clear how to create a fulfillment that's pending.")] - public async Task Opens_Fulfillments() - { - var order = await Fixture.CreateOrder(); - var created = await Fixture.Create(order.Id.Value); - var opened = await Fixture.Service.OpenAsync(order.Id.Value, created.Id.Value); - - Assert.Equal("open", opened.Status); - } - - [Fact(Skip = "Can't complete/cancel/open a fulfillment whose status is not 'pending'. It's not clear how to create a fulfillment that's pending.")] - public async Task Cancels_Fulfillments() - { - var order = await Fixture.CreateOrder(); - var created = await Fixture.Create(order.Id.Value); - var cancelled = await Fixture.Service.CancelAsync(order.Id.Value, created.Id.Value); - - Assert.Equal("cancelled", cancelled.Status); - } - - [Fact(Skip = "Can't complete/cancel/open a fulfillment whose status is not 'pending'. It's not clear how to create a fulfillment that's pending.")] - public async Task Completes_Fulfillments() - { - var order = await Fixture.CreateOrder(); - var created = await Fixture.Create(order.Id.Value); - var cancelled = await Fixture.Service.CancelAsync(order.Id.Value, created.Id.Value); - - Assert.Equal("success", cancelled.Status); - } } public class Fulfillment_Tests_Fixture : IAsyncLifetime { public FulfillmentService Service { get; } = new FulfillmentService(Utils.MyShopifyUrl, Utils.AccessToken); - - public OrderService OrderService { get; } = new OrderService(Utils.MyShopifyUrl, Utils.AccessToken); + private OrderService OrderService { get; } = new OrderService(Utils.MyShopifyUrl, Utils.AccessToken); + private FulfillmentOrderService FulfillmentOrderService { get; } = new FulfillmentOrderService(Utils.MyShopifyUrl, Utils.AccessToken); public long LocationId => 6226758; @@ -369,47 +241,36 @@ public async Task CreateOrder() return obj; } - public async Task Create(long orderId, bool multipleTrackingNumbers = false, IEnumerable items = null) + public async Task> ListFulfillmentOrders(long orderId) { - Fulfillment fulfillment; + var orders = await FulfillmentOrderService.ListAsync(orderId); + + return orders; + } - if (multipleTrackingNumbers) + public async Task Create(long orderId, bool partialFulfillment = false) + { + var fulfillmentOrders = await ListFulfillmentOrders(orderId); + var fulfillment = await Service.CreateAsync(new FulfillmentShipping { - fulfillment = new Fulfillment() + Message = "Items are shipping now!", + FulfillmentRequestOrderLineItems = fulfillmentOrders.Select(o => new LineItemsByFulfillmentOrder { - TrackingCompany = "Jack Black's Pack, Stack and Track", - TrackingUrls = new string[] - { - "https://example.com/da10038ee679f9afc93a785cafdd8d52", - "https://example.com/6349a40313ae3c7544331ff9fb44f28c", - "https://example.com/ca0b2d7bcccec4b58a94a24fa04101d3" - }, - TrackingNumbers = new string[] + FulfillmentOrderId = o.Id.Value, + FulfillmentRequestOrderLineItems = partialFulfillment == false ? null : o.FulfillmentOrderLineItems.Select(li => new FulfillmentRequestOrderLineItem { - "da10038ee679f9afc93a785cafdd8d52", - "6349a40313ae3c7544331ff9fb44f28c", - "ca0b2d7bcccec4b58a94a24fa04101d3" - } - }; - } - else - { - fulfillment = new Fulfillment() + Id = li.LineItemId, + Quantity = li.FulfillableQuantity - 1 + }) + }), + NotifyCustomer = false, + TrackingInfo = new TrackingInfo { - TrackingCompany = "Jack Black's Pack, Stack and Track", - TrackingUrl = "https://example.com/123456789", - TrackingNumber = "123456789", - }; - } - - if (items != null) - { - fulfillment.LineItems = items; - } - - fulfillment.NotifyCustomer = false; - fulfillment.LocationId = LocationId; - fulfillment = await Service.CreateAsync(orderId, fulfillment); + Company = "Jack Black's Pack, Stack and Track", + Url = "https://example.com/123456789", + Number = "123456789" + } + }); Created.Add(fulfillment); From bce273ecd12e042bc2dd6b81cbc50f05d4ee68c0 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Fri, 28 Oct 2022 15:48:58 -0500 Subject: [PATCH 04/12] Refactor FulfillmentService usage to new FulfillmentOrder/FulfillmentService flow --- .../AssignedFulfillmentOrder_Tests.cs | 109 +----------------- .../FulfillmentEvents_Tests .cs | 31 +++-- .../FulfillmentRequest_Tests.cs | 70 ++++------- 3 files changed, 51 insertions(+), 159 deletions(-) diff --git a/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs b/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs index 7b39c4de..a605059c 100644 --- a/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs +++ b/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs @@ -1,6 +1,7 @@ using ShopifySharp.Filters; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -46,14 +47,9 @@ public class AssignedFulfillmentOrder_Tests_Fixture : IAsyncLifetime public FulfillmentService FulfillmentService { get; } = new FulfillmentService(Utils.MyShopifyUrl, Utils.AccessToken); - public OrderService OrderService { get; } = new OrderService(Utils.MyShopifyUrl, Utils.AccessToken); - - public long LocationId => 6226758; + public FulfillmentOrderService FulfillmentOrderService { get; } = new FulfillmentOrderService(Utils.MyShopifyUrl, Utils.AccessToken); - /// - /// Fulfillments must be part of an order and cannot be deleted. - /// - public List CreatedOrders { get; } = new List(); + public OrderService OrderService { get; } = new OrderService(Utils.MyShopifyUrl, Utils.AccessToken); public List CreatedFulfillments { get; } = new List(); @@ -84,104 +80,5 @@ public async Task DisposeAsync() } } } - - public async Task CreateOrder() - { - var obj = await OrderService.CreateAsync(new Order() - { - CreatedAt = DateTime.UtcNow, - BillingAddress = new Address() - { - Address1 = "123 4th Street", - City = "Minneapolis", - Province = "Minnesota", - ProvinceCode = "MN", - Zip = "55401", - Phone = "555-555-5555", - FirstName = "John", - LastName = "Doe", - Company = "Tomorrow Corporation", - Country = "United States", - CountryCode = "US", - Default = true, - }, - LineItems = new List() - { - new LineItem() - { - Name = "Test Line Item", - Title = "Test Line Item Title", - Quantity = 2, - }, - new LineItem() - { - Name = "Test Line Item 2", - Title = "Test Line Item Title 2", - Quantity = 2, - Price = 5 - } - }, - FinancialStatus = "paid", - TotalPrice = 5.00m, - Email = Guid.NewGuid().ToString() + "@example.com", - Note = "Test note about the customer.", - Test = true - }, new OrderCreateOptions() - { - SendReceipt = false, - SendFulfillmentReceipt = false - }); - - CreatedOrders.Add(obj); - - return obj; - } - - public async Task CreateFulfillment(long orderId, bool multipleTrackingNumbers = false, IEnumerable items = null) - { - Fulfillment fulfillment; - - if (multipleTrackingNumbers) - { - fulfillment = new Fulfillment() - { - TrackingCompany = "Jack Black's Pack, Stack and Track", - TrackingUrls = new string[] - { - "https://example.com/da10038ee679f9afc93a785cafdd8d52", - "https://example.com/6349a40313ae3c7544331ff9fb44f28c", - "https://example.com/ca0b2d7bcccec4b58a94a24fa04101d3" - }, - TrackingNumbers = new string[] - { - "da10038ee679f9afc93a785cafdd8d52", - "6349a40313ae3c7544331ff9fb44f28c", - "ca0b2d7bcccec4b58a94a24fa04101d3" - } - }; - } - else - { - fulfillment = new Fulfillment() - { - TrackingCompany = "Jack Black's Pack, Stack and Track", - TrackingUrl = "https://example.com/123456789", - TrackingNumber = "123456789", - }; - } - - if (items != null) - { - fulfillment.LineItems = items; - } - - fulfillment.NotifyCustomer = false; - fulfillment.LocationId = LocationId; - fulfillment = await FulfillmentService.CreateAsync(orderId, fulfillment); - - CreatedFulfillments.Add(fulfillment); - - return fulfillment; - } } } diff --git a/ShopifySharp.Tests/FulfillmentEvents_Tests .cs b/ShopifySharp.Tests/FulfillmentEvents_Tests .cs index 9b364ef8..fc293207 100644 --- a/ShopifySharp.Tests/FulfillmentEvents_Tests .cs +++ b/ShopifySharp.Tests/FulfillmentEvents_Tests .cs @@ -45,6 +45,8 @@ public class FulfillmentEvents_Tests_Fixture : IAsyncLifetime public FulfillmentEventService FulfillmentEventService { get; } = new FulfillmentEventService(Utils.MyShopifyUrl, Utils.AccessToken); public FulfillmentService FulfillmentService { get; } = new FulfillmentService(Utils.MyShopifyUrl, Utils.AccessToken); + + public FulfillmentOrderService FulfillmentOrderService { get; } = new FulfillmentOrderService(Utils.MyShopifyUrl, Utils.AccessToken); public OrderService OrderService { get; } = new OrderService(Utils.MyShopifyUrl, Utils.AccessToken); @@ -65,6 +67,7 @@ public async Task InitializeAsync() // Fulfillment API has a stricter rate limit when on a non-paid store. FulfillmentService.SetExecutionPolicy(policy); + FulfillmentOrderService.SetExecutionPolicy(policy); FulfillmentEventService.SetExecutionPolicy(policy); OrderService.SetExecutionPolicy(policy); @@ -142,16 +145,30 @@ public async Task CreateOrder() return obj; } - public async Task CreateFulfillment(long orderId, bool multipleTrackingNumbers = false, IEnumerable items = null) + public async Task> ListFulfillmentOrders(long orderId) { - var fulfillment = await FulfillmentService.CreateAsync(orderId, new Fulfillment() + var orders = await FulfillmentOrderService.ListAsync(orderId); + + return orders; + } + + public async Task CreateFulfillment(long orderId) + { + var fulfillmentOrders = await ListFulfillmentOrders(orderId); + var fulfillment = await FulfillmentService.CreateAsync(new FulfillmentShipping { - TrackingCompany = "Jack Black's Pack, Stack and Track", - TrackingUrl = "https://example.com/123456789", - TrackingNumber = "123456789", - LineItems = CreatedOrders.First().LineItems, + Message = "Items are shipping now!", + FulfillmentRequestOrderLineItems = fulfillmentOrders.Select(o => new LineItemsByFulfillmentOrder + { + FulfillmentOrderId = o.Id.Value + }), NotifyCustomer = false, - LocationId = LocationId + TrackingInfo = new TrackingInfo + { + Company = "Jack Black's Pack, Stack and Track", + Url = "https://example.com/123456789", + Number = "123456789" + } }); CreatedFulfillments.Add(fulfillment); diff --git a/ShopifySharp.Tests/FulfillmentRequest_Tests.cs b/ShopifySharp.Tests/FulfillmentRequest_Tests.cs index b530e2b2..2cb03b5f 100644 --- a/ShopifySharp.Tests/FulfillmentRequest_Tests.cs +++ b/ShopifySharp.Tests/FulfillmentRequest_Tests.cs @@ -22,7 +22,8 @@ public FulfillmentRequest_Tests(FulfillmentRequest_Tests_Fixture fixture) public async Task Accept_FulfillmentOrders() { var order = await Fixture.CreateOrder(); - var fulfillmentOrder = await Fixture.CreateFulfillmentOrder(order.Id.Value); + var fulfillmentOrders = await Fixture.ListFulfillmentOrders(order.Id.Value); + var fulfillmentOrder = fulfillmentOrders.First(); var result = await Fixture.Service.AcceptAsync(fulfillmentOrder.Id.Value, "Unit Test: Accepted", CancellationToken.None); Assert.NotNull(result); @@ -32,7 +33,8 @@ public async Task Accept_FulfillmentOrders() public async Task Reject_FulfillmentOrders() { var order = await Fixture.CreateOrder(); - var fulfillmentOrder = await Fixture.CreateFulfillmentOrder(order.Id.Value); + var fulfillmentOrders = await Fixture.ListFulfillmentOrders(order.Id.Value); + var fulfillmentOrder = fulfillmentOrders.First(); var result = await Fixture.Service.RejectAsync(fulfillmentOrder.Id.Value, "Unit Test: Rejected", CancellationToken.None); Assert.NotNull(result); @@ -143,59 +145,35 @@ public async Task CreateOrder() return obj; } - async Task CreateFulfillment(long orderId, bool multipleTrackingNumbers = false, IEnumerable items = null) + public async Task> ListFulfillmentOrders(long orderId) { - Fulfillment fulfillment; + var orders = await FulfillmentOrderService.ListAsync(orderId); - if (multipleTrackingNumbers) + return orders; + } + + public async Task CreateFulfillment(long orderId) + { + var fulfillmentOrders = await ListFulfillmentOrders(orderId); + var fulfillment = await FulfillmentService.CreateAsync(new FulfillmentShipping { - fulfillment = new Fulfillment() + Message = "Items are shipping now!", + FulfillmentRequestOrderLineItems = fulfillmentOrders.Select(o => new LineItemsByFulfillmentOrder { - TrackingCompany = "Jack Black's Pack, Stack and Track", - TrackingUrls = new string[] - { - "https://example.com/da10038ee679f9afc93a785cafdd8d52", - "https://example.com/6349a40313ae3c7544331ff9fb44f28c", - "https://example.com/ca0b2d7bcccec4b58a94a24fa04101d3" - }, - TrackingNumbers = new string[] - { - "da10038ee679f9afc93a785cafdd8d52", - "6349a40313ae3c7544331ff9fb44f28c", - "ca0b2d7bcccec4b58a94a24fa04101d3" - } - }; - } - else - { - fulfillment = new Fulfillment() + FulfillmentOrderId = o.Id.Value + }), + NotifyCustomer = false, + TrackingInfo = new TrackingInfo { - TrackingCompany = "Jack Black's Pack, Stack and Track", - TrackingUrl = "https://example.com/123456789", - TrackingNumber = "123456789", - }; - } - - if (items != null) - { - fulfillment.LineItems = items; - } - - fulfillment.NotifyCustomer = false; - fulfillment.LocationId = LocationId; - fulfillment = await FulfillmentService.CreateAsync(orderId, fulfillment); + Company = "Jack Black's Pack, Stack and Track", + Url = "https://example.com/123456789", + Number = "123456789" + } + }); CreatedFulfillments.Add(fulfillment); return fulfillment; } - - public async Task CreateFulfillmentOrder(long orderId) - { - var fulfillment = await CreateFulfillment(orderId); - var fulfillmentOrders = await FulfillmentOrderService.ListAsync(orderId); - - return fulfillmentOrders.First(); - } } } From d61bb26775ca04c4a2c84369a675f776e1574249 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 9 Jan 2023 19:01:10 -0600 Subject: [PATCH 05/12] Fix partial fulfillment order tests --- ShopifySharp.Tests/Fulfillment_Tests.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ShopifySharp.Tests/Fulfillment_Tests.cs b/ShopifySharp.Tests/Fulfillment_Tests.cs index b3494fd1..f042211e 100644 --- a/ShopifySharp.Tests/Fulfillment_Tests.cs +++ b/ShopifySharp.Tests/Fulfillment_Tests.cs @@ -251,18 +251,21 @@ public async Task> ListFulfillmentOrders(long orde public async Task Create(long orderId, bool partialFulfillment = false) { var fulfillmentOrders = await ListFulfillmentOrders(orderId); - var fulfillment = await Service.CreateAsync(new FulfillmentShipping + var lineItems = fulfillmentOrders.Select(o => new LineItemsByFulfillmentOrder { - Message = "Items are shipping now!", - FulfillmentRequestOrderLineItems = fulfillmentOrders.Select(o => new LineItemsByFulfillmentOrder - { - FulfillmentOrderId = o.Id.Value, - FulfillmentRequestOrderLineItems = partialFulfillment == false ? null : o.FulfillmentOrderLineItems.Select(li => new FulfillmentRequestOrderLineItem + FulfillmentOrderId = o.Id.Value, + FulfillmentRequestOrderLineItems = partialFulfillment == false + ? null + : o.FulfillmentOrderLineItems.Select(li => new FulfillmentRequestOrderLineItem { - Id = li.LineItemId, + Id = li.Id, Quantity = li.FulfillableQuantity - 1 }) - }), + }); + var fulfillment = await Service.CreateAsync(new FulfillmentShipping + { + Message = "Items are shipping now!", + FulfillmentRequestOrderLineItems = lineItems, NotifyCustomer = false, TrackingInfo = new TrackingInfo { From 0b6703e0533e4e46c23fdd0f3a545a62575b8ba9 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 9 Jan 2023 19:02:26 -0600 Subject: [PATCH 06/12] Update API version to 2022-07 --- ShopifySharp/Services/ShopifyService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShopifySharp/Services/ShopifyService.cs b/ShopifySharp/Services/ShopifyService.cs index f0f99e60..fb63420d 100644 --- a/ShopifySharp/Services/ShopifyService.cs +++ b/ShopifySharp/Services/ShopifyService.cs @@ -16,7 +16,7 @@ namespace ShopifySharp { public abstract class ShopifyService { - public virtual string APIVersion => "2022-04"; + public virtual string APIVersion => "2022-07"; private static IRequestExecutionPolicy _GlobalExecutionPolicy = new DefaultRequestExecutionPolicy(); From e8e147fa60e96aaa682a5a5fbbb6159d434eb25c Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 9 Jan 2023 20:42:48 -0600 Subject: [PATCH 07/12] Add missing execution policy --- ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs b/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs index a605059c..bc9a7f1d 100644 --- a/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs +++ b/ShopifySharp.Tests/AssignedFulfillmentOrder_Tests.cs @@ -47,8 +47,6 @@ public class AssignedFulfillmentOrder_Tests_Fixture : IAsyncLifetime public FulfillmentService FulfillmentService { get; } = new FulfillmentService(Utils.MyShopifyUrl, Utils.AccessToken); - public FulfillmentOrderService FulfillmentOrderService { get; } = new FulfillmentOrderService(Utils.MyShopifyUrl, Utils.AccessToken); - public OrderService OrderService { get; } = new OrderService(Utils.MyShopifyUrl, Utils.AccessToken); public List CreatedFulfillments { get; } = new List(); @@ -58,6 +56,7 @@ public async Task InitializeAsync() // Fulfillment API has a stricter rate limit when on a non-paid store. var policy = new LeakyBucketExecutionPolicy(); + Service.SetExecutionPolicy(policy); FulfillmentService.SetExecutionPolicy(policy); OrderService.SetExecutionPolicy(policy); From b3c25a3ce040811dcb637e4c934db356ffdc333d Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 9 Jan 2023 20:43:17 -0600 Subject: [PATCH 08/12] Use random collection handle --- ShopifySharp.Tests/Collect_Tests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ShopifySharp.Tests/Collect_Tests.cs b/ShopifySharp.Tests/Collect_Tests.cs index 3becd060..dc8eab53 100644 --- a/ShopifySharp.Tests/Collect_Tests.cs +++ b/ShopifySharp.Tests/Collect_Tests.cs @@ -117,9 +117,11 @@ public async Task InitializeAsync() CustomCollectionService.SetExecutionPolicy(policy); // Create a collection to use with these tests. + var collectionHandle = Guid.NewGuid().ToString(); var collection = await CustomCollectionService.CreateAsync(new CustomCollection() { Title = "Things", + Handle = collectionHandle, Published = false, Image = new CustomCollectionImage() { From f63a4c2599e334730a8e83a8c406b64bc7059acf Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 9 Jan 2023 20:44:33 -0600 Subject: [PATCH 09/12] Disable checkout sales channel tests The checkout sales channel is disabled on ShopifySharp's dev store. Most likely this is due to the dev store's age. --- .../SalesChannel/CheckoutSalesChannel_Tests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs b/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs index e917701a..dd44435e 100644 --- a/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs +++ b/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs @@ -18,7 +18,7 @@ public CheckoutSalesChannel_Tests(CheckoutSalesChannel_Fixture fixture) this.Fixture = fixture; } - [Fact] + [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] public async Task Creates_Checkouts() { var checkout = await Fixture.Create(); @@ -26,7 +26,7 @@ public async Task Creates_Checkouts() Assert.NotNull(checkout); } - [Fact] + [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] public async Task Completes_Checkouts() { var checkout = await Fixture.Create(); @@ -40,7 +40,7 @@ public async Task Completes_Checkouts() Assert.NotNull(checkout); } - [Fact] + [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] public async Task Gets_Checkouts() { var checkout = await Fixture.Create(); @@ -49,7 +49,7 @@ public async Task Gets_Checkouts() Assert.NotNull(checkout); } - [Fact] + [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] public async Task Updates_Checkouts() { var checkout = await Fixture.Create(); @@ -61,7 +61,7 @@ public async Task Updates_Checkouts() Assert.NotNull(checkout.ShippingLine); } - [Fact] + [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] public async Task Lists_Checkout_Shipping_Rates() { var checkout = await Fixture.Create(); @@ -71,7 +71,7 @@ public async Task Lists_Checkout_Shipping_Rates() } - [Fact] + [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] public async Task Stores_CreditCards() { var card = new CreditCard() From cf4be741c37350b5d9ced6eb1f6eda0e3ff639c8 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 9 Jan 2023 20:46:21 -0600 Subject: [PATCH 10/12] Fix datetime serialization issue Using DateTime.MaxValue would throw an ArgumentOutOfRangeException with message "The UTC time represented when the offset is applied must be between year 0 and 10,000." --- ShopifySharp.Tests/TenderTransaction_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ShopifySharp.Tests/TenderTransaction_Tests.cs b/ShopifySharp.Tests/TenderTransaction_Tests.cs index f7fc9d48..3f41c1eb 100644 --- a/ShopifySharp.Tests/TenderTransaction_Tests.cs +++ b/ShopifySharp.Tests/TenderTransaction_Tests.cs @@ -31,7 +31,7 @@ public async Task Lists_TenderTransactions_WithFilter() { var list = await Fixture.Service.ListAsync(new TenderTransactionListFilter { - ProcessedAtMin = DateTime.MaxValue + ProcessedAtMin = DateTime.Now.AddDays(30) }); Assert.True(!list.Items.Any()); From e8d684c338a3860fcfabf600886c136c3d008987 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Mon, 9 Jan 2023 21:12:04 -0600 Subject: [PATCH 11/12] Add execution policy --- ShopifySharp.Tests/Fulfillment_Tests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ShopifySharp.Tests/Fulfillment_Tests.cs b/ShopifySharp.Tests/Fulfillment_Tests.cs index f042211e..b7300cd8 100644 --- a/ShopifySharp.Tests/Fulfillment_Tests.cs +++ b/ShopifySharp.Tests/Fulfillment_Tests.cs @@ -164,6 +164,7 @@ public async Task InitializeAsync() Service.SetExecutionPolicy(policy); OrderService.SetExecutionPolicy(policy); + FulfillmentOrderService.SetExecutionPolicy(policy); // Create an order and fulfillment for count, list, get, etc. tests. var order = await CreateOrder(); From d08360a644214edc818db8b1d072a7e745b48d29 Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Tue, 10 Jan 2023 16:23:01 -0600 Subject: [PATCH 12/12] Disable sales channel tests Private/custom apps cannot use sales channel APIs and therefore the tests can't be run. --- .../SalesChannel/CheckoutSalesChannel_Tests.cs | 13 ++++++------- .../SalesChannel/CollectionListing_Tests.cs | 7 +++---- .../SalesChannel/ProductListing_Tests.cs | 5 +++-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs b/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs index dd44435e..3c1b5eb4 100644 --- a/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs +++ b/ShopifySharp.Tests/SalesChannel/CheckoutSalesChannel_Tests.cs @@ -18,7 +18,7 @@ public CheckoutSalesChannel_Tests(CheckoutSalesChannel_Fixture fixture) this.Fixture = fixture; } - [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Creates_Checkouts() { var checkout = await Fixture.Create(); @@ -26,7 +26,7 @@ public async Task Creates_Checkouts() Assert.NotNull(checkout); } - [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Completes_Checkouts() { var checkout = await Fixture.Create(); @@ -40,7 +40,7 @@ public async Task Completes_Checkouts() Assert.NotNull(checkout); } - [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Gets_Checkouts() { var checkout = await Fixture.Create(); @@ -49,7 +49,7 @@ public async Task Gets_Checkouts() Assert.NotNull(checkout); } - [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Updates_Checkouts() { var checkout = await Fixture.Create(); @@ -61,7 +61,7 @@ public async Task Updates_Checkouts() Assert.NotNull(checkout.ShippingLine); } - [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Lists_Checkout_Shipping_Rates() { var checkout = await Fixture.Create(); @@ -70,8 +70,7 @@ public async Task Lists_Checkout_Shipping_Rates() Assert.NotEmpty(shippingRates); } - - [Fact(Skip = "Checkouts are disabled on the ShopifySharp development store.")] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Stores_CreditCards() { var card = new CreditCard() diff --git a/ShopifySharp.Tests/SalesChannel/CollectionListing_Tests.cs b/ShopifySharp.Tests/SalesChannel/CollectionListing_Tests.cs index d70c4db8..78958fea 100644 --- a/ShopifySharp.Tests/SalesChannel/CollectionListing_Tests.cs +++ b/ShopifySharp.Tests/SalesChannel/CollectionListing_Tests.cs @@ -18,7 +18,7 @@ public CollectionListing_Tests(CollectionListing_Tests_Fixture fixture) this.Fixture = fixture; } - [Fact] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Lists_Collections_NoFilter() { var list = await Fixture.Service.ListAsync(); @@ -32,7 +32,7 @@ public async Task Lists_Collections_NoFilter() } } - [Fact] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Lists_Collection_ProductIds_NoFilter() { var list = await Fixture.Service.ListAsync(); @@ -49,8 +49,7 @@ public async Task Lists_Collection_ProductIds_NoFilter() } } - - [Fact] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Retrieve_Specific_Collection() { var list = await Fixture.Service.ListAsync(); diff --git a/ShopifySharp.Tests/SalesChannel/ProductListing_Tests.cs b/ShopifySharp.Tests/SalesChannel/ProductListing_Tests.cs index 8263726e..4252ad7a 100644 --- a/ShopifySharp.Tests/SalesChannel/ProductListing_Tests.cs +++ b/ShopifySharp.Tests/SalesChannel/ProductListing_Tests.cs @@ -17,7 +17,8 @@ public ProductListing_Tests(ProductListing_Tests_Fixture fixture) { this.Fixture = fixture; } - [Fact] + + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Counts_Products() { var count = await Fixture.Service.CountAsync(); @@ -25,7 +26,7 @@ public async Task Counts_Products() Assert.True(count > 0); } - [Fact] + [Fact(Skip = "Sales channel tests cannot be run with ShopifySharp's private/custom app.")] public async Task Lists_Products_NoFilter() { var list = await Fixture.Service.ListAsync();