From c7a8e3ffbc0c6941356e2df1943dcaec502a6cfd Mon Sep 17 00:00:00 2001 From: Alexey Afanasev Date: Thu, 1 Jun 2017 18:03:45 +0300 Subject: [PATCH 1/2] Introduce PUT for shipping methods --- .../reference/resources/customer_cart.apib | 8 ++ .../phoenix/app/phoenix/routes/Customer.scala | 5 + .../app/phoenix/routes/admin/CartRoutes.scala | 7 ++ .../AddressesIntegrationTest.scala | 104 +++++++++++++----- .../integration/testutils/HttpSupport.scala | 14 +++ .../testutils/apis/PhoenixStorefrontApi.scala | 18 +++ 6 files changed, 129 insertions(+), 27 deletions(-) diff --git a/developer-portal/content/reference/resources/customer_cart.apib b/developer-portal/content/reference/resources/customer_cart.apib index 863c1bd217..2fd04f9859 100644 --- a/developer-portal/content/reference/resources/customer_cart.apib +++ b/developer-portal/content/reference/resources/customer_cart.apib @@ -151,6 +151,14 @@ Removes the shipping method from the cart. + Attributes (UpdateAddressPayload) + Response 200 (application/json) + Attributes (FullOrder) + + +### Create or update [PUT /v1/my/cart/shipping-address] + ++ Request (application/json) + + Attributes(CreateAddressPayload) ++ Response 200 (application/json) + + Attributes (Address) ### Delete [DELETE] diff --git a/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala b/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala index eae068def5..5d017c9b3c 100644 --- a/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala +++ b/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala @@ -139,6 +139,11 @@ object Customer { CartShippingAddressUpdater.updateShippingAddressFromPayload(auth.model, payload) } } ~ + (put & pathEnd & entity(as[CreateAddressPayload])) { payload ⇒ + mutateOrFailures { + CartShippingAddressUpdater.createShippingAddressFromPayload(auth.model, payload) + } + } ~ (delete & pathEnd) { deleteOrFailures { CartShippingAddressUpdater.removeShippingAddress(auth.model) diff --git a/phoenix-scala/phoenix/app/phoenix/routes/admin/CartRoutes.scala b/phoenix-scala/phoenix/app/phoenix/routes/admin/CartRoutes.scala index 8c6e85e3e4..966677f896 100644 --- a/phoenix-scala/phoenix/app/phoenix/routes/admin/CartRoutes.scala +++ b/phoenix-scala/phoenix/app/phoenix/routes/admin/CartRoutes.scala @@ -145,6 +145,13 @@ object CartRoutes { Some(refNum)) } } ~ + (put & pathEnd & entity(as[CreateAddressPayload])) { payload ⇒ + mutateOrFailures { + CartShippingAddressUpdater.createShippingAddressFromPayload(auth.model, + payload, + Some(refNum)) + } + } ~ (delete & pathEnd) { mutateOrFailures { CartShippingAddressUpdater.removeShippingAddress(auth.model, Some(refNum)) diff --git a/phoenix-scala/phoenix/test/integration/AddressesIntegrationTest.scala b/phoenix-scala/phoenix/test/integration/AddressesIntegrationTest.scala index 788b2b777d..2392fabe75 100644 --- a/phoenix-scala/phoenix/test/integration/AddressesIntegrationTest.scala +++ b/phoenix-scala/phoenix/test/integration/AddressesIntegrationTest.scala @@ -5,10 +5,12 @@ import phoenix.models.account._ import phoenix.models.cord.OrderShippingAddresses import phoenix.models.location.{Address, Addresses, Country, Region} import phoenix.payloads.AddressPayloads.CreateAddressPayload -import phoenix.responses.AddressResponse +import phoenix.payloads.CartPayloads.CreateCart +import phoenix.responses.{AddressResponse, TheResponse} import phoenix.responses.PublicResponses.CountryWithRegions +import phoenix.responses.cord.CartResponse import testutils._ -import testutils.apis.{PhoenixAdminApi, PhoenixPublicApi} +import testutils.apis.{PhoenixAdminApi, PhoenixPublicApi, PhoenixStorefrontApi} import testutils.fixtures.BakedFixtures import testutils.fixtures.api.{randomAddress, ApiFixtureHelpers} @@ -18,6 +20,7 @@ class AddressesIntegrationTest with DefaultJwtAdminAuth with ApiFixtureHelpers with PhoenixAdminApi + with PhoenixStorefrontApi with PhoenixPublicApi with BakedFixtures { @@ -31,15 +34,10 @@ class AddressesIntegrationTest } "POST /v1/customers/:customerId/addresses" - { - "creates an address" in new Customer_Seed { - val payload = CreateAddressPayload(name = "Home Office", - regionId = 1, - address1 = "3000 Coolio Dr", - city = "Seattle", - zip = "55555") + "creates an address" in new Customer_Seed with AddressFixture { val newAddress = - customersApi(customer.accountId).addresses.create(payload).as[AddressResponse] - newAddress.name must === (payload.name) + customersApi(customer.accountId).addresses.create(addressPayload).as[AddressResponse] + newAddress.name must === (addressPayload.name) newAddress.isDefault must === (Some(false)) } } @@ -74,31 +72,23 @@ class AddressesIntegrationTest } "PATCH /v1/customers/:customerId/addresses/:addressId" - { - "can be edited" in new CustomerAddress_Baked { - val payload = CreateAddressPayload(name = "Home Office", - regionId = 1, - address1 = "3000 Coolio Dr", - city = "Seattle", - zip = "55555") - (payload.name, payload.address1) must !==((address.name, address.address1)) + "can be edited" in new CustomerAddress_Baked with AddressFixture { + (addressPayload.name, addressPayload.address1) must !==((address.name, address.address1)) - val updated = - customersApi(customer.accountId).address(address.id).edit(payload).as[AddressResponse] + val updated = customersApi(customer.accountId) + .address(address.id) + .edit(addressPayload) + .as[AddressResponse] - (updated.name, updated.address1) must === ((payload.name, payload.address1)) + (updated.name, updated.address1) must === ((addressPayload.name, addressPayload.address1)) } } "DELETE /v1/customers/:customerId/addresses/:addressId" - { - "can be deleted" in new CustomerAddress_Baked { + "can be deleted" in new CustomerAddress_Baked with AddressFixture { //notice the payload is a default shipping address. Delete should make it not default. - val payload = CreateAddressPayload(name = "Delete Me", - regionId = 1, - address1 = "5000 Delete Dr", - city = "Deattle", - zip = "666", - isDefault = true) + val payload = addressPayload.copy(isDefault = true) val newAddress: AddressResponse = customersApi(customer.accountId).addresses.create(payload).as[AddressResponse] @@ -144,6 +134,57 @@ class AddressesIntegrationTest } } + "Create /v1/my/addresses" - { + "POST shipping addresses into a cart adds it to customer details as well" in new AddressFixture { + withNewCustomerAuth(TestLoginData.random) { implicit auth ⇒ + val cart = cartsApi.create(CreateCart(customerId = auth.customerId.some)).as[CartResponse] + + storefrontCartsApi.shippingAddress.create(addressPayload).as[TheResponse[CartResponse]] + + customersApi(auth.customerId).addresses.get + .as[Seq[AddressResponse]] + .onlyElement + .address1 must === (addressPayload.address1) + + cartsApi(cart.referenceNumber) + .get() + .asTheResult[CartResponse] + .shippingAddress + .value + .address1 must === (addressPayload.address1) + } + } + + "PUT for shipping addresses must be idempotent" in new AddressFixture { + pending // PR #2036 + withNewCustomerAuth(TestLoginData.random) { implicit auth ⇒ + val cart = cartsApi.create(CreateCart(customerId = auth.customerId.some)).as[CartResponse] + + storefrontCartsApi.shippingAddress.createOrUpdate(addressPayload).mustBeOk() + storefrontCartsApi.shippingAddress.createOrUpdate(addressPayload).mustBeOk() + storefrontCartsApi.shippingAddress + .createOrUpdate(addressPayload.copy(address1 = "My New address")) + .mustBeOk() + + val shippingAddress = cartsApi(cart.referenceNumber) + .get() + .asTheResult[CartResponse] + .shippingAddress + .value match { + case adr ⇒ (adr.address1, adr.city, adr.zip) + } + + val customerAddress = + customersApi(auth.customerId).addresses.get.as[Seq[AddressResponse]].onlyElement match { + case adr ⇒ (adr.address1, adr.city, adr.zip) + } + + shippingAddress must === (("My New address", addressPayload.city, addressPayload.zip)) + customerAddress must === (("My New address", addressPayload.city, addressPayload.zip)) + } + } + } + "GET /v1/my/addresses" - { "retrieves a customer's addresses" in { val (customer, loginData) = api_newCustomerWithLogin() @@ -207,4 +248,13 @@ class AddressesIntegrationTest trait NoDefaultAddressFixture extends CustomerAddress_Baked with EmptyCustomerCart_Baked { val shippingAddress = OrderShippingAddresses.copyFromAddress(address, cart.refNum).gimme } + + trait AddressFixture { + val addressPayload = CreateAddressPayload(name = "Home Office", + regionId = 1, + address1 = "3000 Coolio Dr", + city = "Seattle", + zip = "55555") + } + } diff --git a/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala b/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala index ed3dcc8bcb..88f1a292d8 100644 --- a/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala +++ b/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala @@ -118,6 +118,17 @@ trait HttpSupport dispatchRequest(request, jwtCookie) } + def PUT(path: String, rawBody: String, jwtCookie: Option[Cookie]): HttpResponse = { + val request = HttpRequest(method = HttpMethods.PUT, + uri = pathToAbsoluteUrl(path), + entity = HttpEntity.Strict( + ContentTypes.`application/json`, + ByteString(rawBody) + )) + + dispatchRequest(request, jwtCookie) + } + def POST(path: String, jwtCookie: Option[Cookie]): HttpResponse = dispatchRequest(HttpRequest(method = HttpMethods.POST, uri = pathToAbsoluteUrl(path)), jwtCookie) @@ -141,6 +152,9 @@ trait HttpSupport def POST[T <: AnyRef](path: String, payload: T, jwtCookie: Option[Cookie]): HttpResponse = POST(path, writeJson(payload), jwtCookie) + def PUT[T <: AnyRef](path: String, payload: T, jwtCookie: Option[Cookie]): HttpResponse = + PUT(path, writeJson(payload), jwtCookie) + def PATCH[T <: AnyRef](path: String, payload: T, jwtCookie: Option[Cookie]): HttpResponse = PATCH(path, writeJson(payload), jwtCookie) diff --git a/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala b/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala index 39550feddc..94d9580ce2 100644 --- a/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala +++ b/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala @@ -2,6 +2,7 @@ package testutils.apis import akka.http.scaladsl.model.HttpResponse import cats.implicits._ +import phoenix.payloads.AddressPayloads.CreateAddressPayload import phoenix.payloads.CartPayloads.CheckoutCart import phoenix.payloads.LineItemPayloads.UpdateLineItemsPayload import phoenix.payloads.PaymentPayloads.{CreateApplePayPayment, CreateCreditCardFromTokenPayload} @@ -11,6 +12,13 @@ trait PhoenixStorefrontApi extends HttpSupport { self: FoxSuite ⇒ val rootPrefix: String = "v1/my" + object accountApi { + val accountPath = s"$rootPrefix/account" + + def getAccount()(implicit ca: TestCustomerAuth): HttpResponse = + GET(accountPath, ca.jwtCookie.some) + } + case class storefrontProductsApi(reference: String) { val productPath = s"$rootPrefix/products/$reference/baked" @@ -42,7 +50,17 @@ trait PhoenixStorefrontApi extends HttpSupport { self: FoxSuite ⇒ def searchByRegion(countryCode: String)(implicit aa: TestCustomerAuth): HttpResponse = GET(s"$shippingMethods/$countryCode", aa.jwtCookie.some) + } + + object shippingAddress { + val shippingAddress = s"$cartPath/shipping-address" + + def create(payload: CreateAddressPayload)(implicit ca: TestCustomerAuth): HttpResponse = + POST(shippingAddress, payload, ca.jwtCookie.some) + def createOrUpdate(payload: CreateAddressPayload)( + implicit ca: TestCustomerAuth): HttpResponse = + PUT(shippingAddress, payload, ca.jwtCookie.some) } } From 7f3e95525d1e4e2a04d3c97cd2a817ad115b2e70 Mon Sep 17 00:00:00 2001 From: Alexey Afanasev Date: Thu, 15 Jun 2017 12:40:31 +0300 Subject: [PATCH 2/2] Formatting fixed --- .../phoenix/test/integration/testutils/HttpSupport.scala | 4 ++-- .../integration/testutils/apis/PhoenixStorefrontApi.scala | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala b/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala index 88f1a292d8..083e17ba01 100644 --- a/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala +++ b/phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala @@ -122,8 +122,8 @@ trait HttpSupport val request = HttpRequest(method = HttpMethods.PUT, uri = pathToAbsoluteUrl(path), entity = HttpEntity.Strict( - ContentTypes.`application/json`, - ByteString(rawBody) + ContentTypes.`application/json`, + ByteString(rawBody) )) dispatchRequest(request, jwtCookie) diff --git a/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala b/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala index 94d9580ce2..a345514fbf 100644 --- a/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala +++ b/phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala @@ -58,8 +58,7 @@ trait PhoenixStorefrontApi extends HttpSupport { self: FoxSuite ⇒ def create(payload: CreateAddressPayload)(implicit ca: TestCustomerAuth): HttpResponse = POST(shippingAddress, payload, ca.jwtCookie.some) - def createOrUpdate(payload: CreateAddressPayload)( - implicit ca: TestCustomerAuth): HttpResponse = + def createOrUpdate(payload: CreateAddressPayload)(implicit ca: TestCustomerAuth): HttpResponse = PUT(shippingAddress, payload, ca.jwtCookie.some) } }