Skip to content

Commit c7a8e3f

Browse files
committed
Introduce PUT for shipping methods
1 parent af03c31 commit c7a8e3f

6 files changed

Lines changed: 129 additions & 27 deletions

File tree

developer-portal/content/reference/resources/customer_cart.apib

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ Removes the shipping method from the cart.
151151
+ Attributes (UpdateAddressPayload)
152152
+ Response 200 (application/json)
153153
+ Attributes (FullOrder)
154+
155+
156+
### Create or update [PUT /v1/my/cart/shipping-address]
157+
158+
+ Request (application/json)
159+
+ Attributes(CreateAddressPayload)
160+
+ Response 200 (application/json)
161+
+ Attributes (Address)
154162

155163
### Delete [DELETE]
156164

phoenix-scala/phoenix/app/phoenix/routes/Customer.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ object Customer {
139139
CartShippingAddressUpdater.updateShippingAddressFromPayload(auth.model, payload)
140140
}
141141
} ~
142+
(put & pathEnd & entity(as[CreateAddressPayload])) { payload
143+
mutateOrFailures {
144+
CartShippingAddressUpdater.createShippingAddressFromPayload(auth.model, payload)
145+
}
146+
} ~
142147
(delete & pathEnd) {
143148
deleteOrFailures {
144149
CartShippingAddressUpdater.removeShippingAddress(auth.model)

phoenix-scala/phoenix/app/phoenix/routes/admin/CartRoutes.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ object CartRoutes {
145145
Some(refNum))
146146
}
147147
} ~
148+
(put & pathEnd & entity(as[CreateAddressPayload])) { payload
149+
mutateOrFailures {
150+
CartShippingAddressUpdater.createShippingAddressFromPayload(auth.model,
151+
payload,
152+
Some(refNum))
153+
}
154+
} ~
148155
(delete & pathEnd) {
149156
mutateOrFailures {
150157
CartShippingAddressUpdater.removeShippingAddress(auth.model, Some(refNum))

phoenix-scala/phoenix/test/integration/AddressesIntegrationTest.scala

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import phoenix.models.account._
55
import phoenix.models.cord.OrderShippingAddresses
66
import phoenix.models.location.{Address, Addresses, Country, Region}
77
import phoenix.payloads.AddressPayloads.CreateAddressPayload
8-
import phoenix.responses.AddressResponse
8+
import phoenix.payloads.CartPayloads.CreateCart
9+
import phoenix.responses.{AddressResponse, TheResponse}
910
import phoenix.responses.PublicResponses.CountryWithRegions
11+
import phoenix.responses.cord.CartResponse
1012
import testutils._
11-
import testutils.apis.{PhoenixAdminApi, PhoenixPublicApi}
13+
import testutils.apis.{PhoenixAdminApi, PhoenixPublicApi, PhoenixStorefrontApi}
1214
import testutils.fixtures.BakedFixtures
1315
import testutils.fixtures.api.{randomAddress, ApiFixtureHelpers}
1416

@@ -18,6 +20,7 @@ class AddressesIntegrationTest
1820
with DefaultJwtAdminAuth
1921
with ApiFixtureHelpers
2022
with PhoenixAdminApi
23+
with PhoenixStorefrontApi
2124
with PhoenixPublicApi
2225
with BakedFixtures {
2326

@@ -31,15 +34,10 @@ class AddressesIntegrationTest
3134
}
3235

3336
"POST /v1/customers/:customerId/addresses" - {
34-
"creates an address" in new Customer_Seed {
35-
val payload = CreateAddressPayload(name = "Home Office",
36-
regionId = 1,
37-
address1 = "3000 Coolio Dr",
38-
city = "Seattle",
39-
zip = "55555")
37+
"creates an address" in new Customer_Seed with AddressFixture {
4038
val newAddress =
41-
customersApi(customer.accountId).addresses.create(payload).as[AddressResponse]
42-
newAddress.name must === (payload.name)
39+
customersApi(customer.accountId).addresses.create(addressPayload).as[AddressResponse]
40+
newAddress.name must === (addressPayload.name)
4341
newAddress.isDefault must === (Some(false))
4442
}
4543
}
@@ -74,31 +72,23 @@ class AddressesIntegrationTest
7472
}
7573

7674
"PATCH /v1/customers/:customerId/addresses/:addressId" - {
77-
"can be edited" in new CustomerAddress_Baked {
78-
val payload = CreateAddressPayload(name = "Home Office",
79-
regionId = 1,
80-
address1 = "3000 Coolio Dr",
81-
city = "Seattle",
82-
zip = "55555")
83-
(payload.name, payload.address1) must !==((address.name, address.address1))
75+
"can be edited" in new CustomerAddress_Baked with AddressFixture {
76+
(addressPayload.name, addressPayload.address1) must !==((address.name, address.address1))
8477

85-
val updated =
86-
customersApi(customer.accountId).address(address.id).edit(payload).as[AddressResponse]
78+
val updated = customersApi(customer.accountId)
79+
.address(address.id)
80+
.edit(addressPayload)
81+
.as[AddressResponse]
8782

88-
(updated.name, updated.address1) must === ((payload.name, payload.address1))
83+
(updated.name, updated.address1) must === ((addressPayload.name, addressPayload.address1))
8984
}
9085
}
9186

9287
"DELETE /v1/customers/:customerId/addresses/:addressId" - {
93-
"can be deleted" in new CustomerAddress_Baked {
88+
"can be deleted" in new CustomerAddress_Baked with AddressFixture {
9489

9590
//notice the payload is a default shipping address. Delete should make it not default.
96-
val payload = CreateAddressPayload(name = "Delete Me",
97-
regionId = 1,
98-
address1 = "5000 Delete Dr",
99-
city = "Deattle",
100-
zip = "666",
101-
isDefault = true)
91+
val payload = addressPayload.copy(isDefault = true)
10292

10393
val newAddress: AddressResponse =
10494
customersApi(customer.accountId).addresses.create(payload).as[AddressResponse]
@@ -144,6 +134,57 @@ class AddressesIntegrationTest
144134
}
145135
}
146136

137+
"Create /v1/my/addresses" - {
138+
"POST shipping addresses into a cart adds it to customer details as well" in new AddressFixture {
139+
withNewCustomerAuth(TestLoginData.random) { implicit auth
140+
val cart = cartsApi.create(CreateCart(customerId = auth.customerId.some)).as[CartResponse]
141+
142+
storefrontCartsApi.shippingAddress.create(addressPayload).as[TheResponse[CartResponse]]
143+
144+
customersApi(auth.customerId).addresses.get
145+
.as[Seq[AddressResponse]]
146+
.onlyElement
147+
.address1 must === (addressPayload.address1)
148+
149+
cartsApi(cart.referenceNumber)
150+
.get()
151+
.asTheResult[CartResponse]
152+
.shippingAddress
153+
.value
154+
.address1 must === (addressPayload.address1)
155+
}
156+
}
157+
158+
"PUT for shipping addresses must be idempotent" in new AddressFixture {
159+
pending // PR #2036
160+
withNewCustomerAuth(TestLoginData.random) { implicit auth
161+
val cart = cartsApi.create(CreateCart(customerId = auth.customerId.some)).as[CartResponse]
162+
163+
storefrontCartsApi.shippingAddress.createOrUpdate(addressPayload).mustBeOk()
164+
storefrontCartsApi.shippingAddress.createOrUpdate(addressPayload).mustBeOk()
165+
storefrontCartsApi.shippingAddress
166+
.createOrUpdate(addressPayload.copy(address1 = "My New address"))
167+
.mustBeOk()
168+
169+
val shippingAddress = cartsApi(cart.referenceNumber)
170+
.get()
171+
.asTheResult[CartResponse]
172+
.shippingAddress
173+
.value match {
174+
case adr (adr.address1, adr.city, adr.zip)
175+
}
176+
177+
val customerAddress =
178+
customersApi(auth.customerId).addresses.get.as[Seq[AddressResponse]].onlyElement match {
179+
case adr (adr.address1, adr.city, adr.zip)
180+
}
181+
182+
shippingAddress must === (("My New address", addressPayload.city, addressPayload.zip))
183+
customerAddress must === (("My New address", addressPayload.city, addressPayload.zip))
184+
}
185+
}
186+
}
187+
147188
"GET /v1/my/addresses" - {
148189
"retrieves a customer's addresses" in {
149190
val (customer, loginData) = api_newCustomerWithLogin()
@@ -207,4 +248,13 @@ class AddressesIntegrationTest
207248
trait NoDefaultAddressFixture extends CustomerAddress_Baked with EmptyCustomerCart_Baked {
208249
val shippingAddress = OrderShippingAddresses.copyFromAddress(address, cart.refNum).gimme
209250
}
251+
252+
trait AddressFixture {
253+
val addressPayload = CreateAddressPayload(name = "Home Office",
254+
regionId = 1,
255+
address1 = "3000 Coolio Dr",
256+
city = "Seattle",
257+
zip = "55555")
258+
}
259+
210260
}

phoenix-scala/phoenix/test/integration/testutils/HttpSupport.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ trait HttpSupport
118118
dispatchRequest(request, jwtCookie)
119119
}
120120

121+
def PUT(path: String, rawBody: String, jwtCookie: Option[Cookie]): HttpResponse = {
122+
val request = HttpRequest(method = HttpMethods.PUT,
123+
uri = pathToAbsoluteUrl(path),
124+
entity = HttpEntity.Strict(
125+
ContentTypes.`application/json`,
126+
ByteString(rawBody)
127+
))
128+
129+
dispatchRequest(request, jwtCookie)
130+
}
131+
121132
def POST(path: String, jwtCookie: Option[Cookie]): HttpResponse =
122133
dispatchRequest(HttpRequest(method = HttpMethods.POST, uri = pathToAbsoluteUrl(path)), jwtCookie)
123134

@@ -141,6 +152,9 @@ trait HttpSupport
141152
def POST[T <: AnyRef](path: String, payload: T, jwtCookie: Option[Cookie]): HttpResponse =
142153
POST(path, writeJson(payload), jwtCookie)
143154

155+
def PUT[T <: AnyRef](path: String, payload: T, jwtCookie: Option[Cookie]): HttpResponse =
156+
PUT(path, writeJson(payload), jwtCookie)
157+
144158
def PATCH[T <: AnyRef](path: String, payload: T, jwtCookie: Option[Cookie]): HttpResponse =
145159
PATCH(path, writeJson(payload), jwtCookie)
146160

phoenix-scala/phoenix/test/integration/testutils/apis/PhoenixStorefrontApi.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package testutils.apis
22

33
import akka.http.scaladsl.model.HttpResponse
44
import cats.implicits._
5+
import phoenix.payloads.AddressPayloads.CreateAddressPayload
56
import phoenix.payloads.CartPayloads.CheckoutCart
67
import phoenix.payloads.LineItemPayloads.UpdateLineItemsPayload
78
import phoenix.payloads.PaymentPayloads.{CreateApplePayPayment, CreateCreditCardFromTokenPayload}
@@ -11,6 +12,13 @@ trait PhoenixStorefrontApi extends HttpSupport { self: FoxSuite ⇒
1112

1213
val rootPrefix: String = "v1/my"
1314

15+
object accountApi {
16+
val accountPath = s"$rootPrefix/account"
17+
18+
def getAccount()(implicit ca: TestCustomerAuth): HttpResponse =
19+
GET(accountPath, ca.jwtCookie.some)
20+
}
21+
1422
case class storefrontProductsApi(reference: String) {
1523
val productPath = s"$rootPrefix/products/$reference/baked"
1624

@@ -42,7 +50,17 @@ trait PhoenixStorefrontApi extends HttpSupport { self: FoxSuite ⇒
4250

4351
def searchByRegion(countryCode: String)(implicit aa: TestCustomerAuth): HttpResponse =
4452
GET(s"$shippingMethods/$countryCode", aa.jwtCookie.some)
53+
}
54+
55+
object shippingAddress {
56+
val shippingAddress = s"$cartPath/shipping-address"
57+
58+
def create(payload: CreateAddressPayload)(implicit ca: TestCustomerAuth): HttpResponse =
59+
POST(shippingAddress, payload, ca.jwtCookie.some)
4560

61+
def createOrUpdate(payload: CreateAddressPayload)(
62+
implicit ca: TestCustomerAuth): HttpResponse =
63+
PUT(shippingAddress, payload, ca.jwtCookie.some)
4664
}
4765
}
4866

0 commit comments

Comments
 (0)