diff --git a/features/get_cart_detail.feature b/features/get_cart_detail.feature new file mode 100644 index 0000000..765686a --- /dev/null +++ b/features/get_cart_detail.feature @@ -0,0 +1,37 @@ +Feature: Get cart detail + + Background: + Given the following products exist: + | sku | name | price | + | 001 | potatoes | 2.5 | + | 002 | water | 0.95 | + And there is a cart discount "free shipping" for 10 % with code "FREE-SHIPPING" + And I have a cart + And I add 2 units of product "001" to my cart + And I add 1 units of product "002" to my cart + And I apply "FREE-SHIPPING" discount to my cart + + Scenario: Get cart details + When I send a "GET" request to "/carts/1" + Then the response status should be 200 with body: + """ + { + "id": 1, + "total": 5.36, + "lines": [ + { + "sku": "001", + "name": "potatoes", + "quantity": 2 + }, + { + "sku": "002", + "name": "water", + "quantity": 1 + } + ], + "discounts": [ + "free shipping" + ] + } + """ \ No newline at end of file diff --git a/src/application.ts b/src/application.ts index 395782b..59f33a7 100644 --- a/src/application.ts +++ b/src/application.ts @@ -1,6 +1,7 @@ import { DataSource, Repository } from "typeorm" import { SnakeNamingStrategy } from "typeorm-naming-strategies" import CartDiscountAdder from "./application/CartDiscountAdder" +import CartFetcher from "./application/CartFetcher" import CartLineAdder from "./application/CartlineAdder" import Server from "./config/Server" import CartController from "./controller/CartController" @@ -26,7 +27,8 @@ const productRepository: Repository = connection.getRepository(Product) const discountRepository: Repository = connection.getRepository(Discount) const cartLineAdder: CartLineAdder = new CartLineAdder(cartRepository, productRepository) const cartDiscountAdder: CartDiscountAdder = new CartDiscountAdder(cartRepository, discountRepository) -const cartController = new CartController(cartLineAdder, cartDiscountAdder) +const cartFetcher: CartFetcher = new CartFetcher(cartRepository) +const cartController = new CartController(cartLineAdder, cartDiscountAdder, cartFetcher) const server: Server = new Server(process.env.WEB_PORT, [cartController]) connection.initialize() diff --git a/src/application/CartFetcher.ts b/src/application/CartFetcher.ts new file mode 100644 index 0000000..888b8cc --- /dev/null +++ b/src/application/CartFetcher.ts @@ -0,0 +1,52 @@ +import { Repository } from "typeorm" +import Cart from "../entity/Cart" +import CartLine from "../entity/CartLine" +import Discount from "../entity/Discount" + + +class CartFetcher { + private cartRepository: Repository + + constructor(cartRepository: Repository) { + this.cartRepository = cartRepository + } + + public async fetch(cartId: number): Promise { + const cart: Cart = await this.cartRepository.findOneByOrFail({id: cartId}) + + return new CartDetail( + cart.id, + cart.totalPrice(), + cart.lines.map((line: CartLine) => new Line(line.product.sku, line.product.name, line.quantity)), + cart.discounts.map((discount: Discount) => discount.name) + ) + } +} + +class CartDetail { + public id: number + public total: number + public lines: Line[] + public discounts: string[] + + constructor(id: number, total:number, lines: Line[], discounts: string[]) { + this.id = id + this.total = total + this.lines = lines + this.discounts = discounts + } +} + +class Line { + public sku: string + public name: string + public quantity: number + + constructor(sku: string, name: string, quantity: number) { + this.sku = sku + this.name = name + this.quantity = quantity + } +} + +export default CartFetcher \ No newline at end of file diff --git a/src/controller/CartController.ts b/src/controller/CartController.ts index 9b4a015..b87286e 100644 --- a/src/controller/CartController.ts +++ b/src/controller/CartController.ts @@ -2,14 +2,17 @@ import { Application, Request, Response } from "express" import Controller from "../config/Controller" import CartDiscountAdder from "../application/CartDiscountAdder" import CartLineAdder from "../application/CartlineAdder" +import CartFetcher from "../application/CartFetcher" class CartController implements Controller { private cartLineAdder: CartLineAdder private cartDiscountAdder: CartDiscountAdder + private cartFetcher: CartFetcher - constructor(cartLineAdder: CartLineAdder, cartDiscountAdder: CartDiscountAdder) { + constructor(cartLineAdder: CartLineAdder, cartDiscountAdder: CartDiscountAdder, cartFetcher: CartFetcher) { this.cartLineAdder = cartLineAdder this.cartDiscountAdder = cartDiscountAdder + this.cartFetcher = cartFetcher } private addLine = async (request: Request, response: Response): Promise => { @@ -30,9 +33,18 @@ class CartController implements Controller { response.status(200).send() } + private recoverCartDetail = async (request: Request, response: Response): Promise => { + const { id } = request.params + + const cart = await this.cartFetcher.fetch(+id) + + response.status(200).send(cart) + } + public addRoutes(app: Application): void { app.post("/carts/:id/lines", this.addLine) app.post("/carts/:id/discounts", this.addDiscount) + app.get("/carts/:id", this.recoverCartDetail) } } diff --git a/src/tests/steps/HttpSteps.ts b/src/tests/steps/HttpSteps.ts new file mode 100644 index 0000000..783f03a --- /dev/null +++ b/src/tests/steps/HttpSteps.ts @@ -0,0 +1,35 @@ +import { before, binding, then, when } from "cucumber-tsflow" +import axios, { Axios, AxiosResponse } from 'axios' +import assert from "assert" + +@binding() +class HttpSteps { + private axios: Axios + private response: AxiosResponse|undefined; + + constructor() { + this.axios = axios + } + + @before() + public setUp(): void { + this.response = undefined; + } + + @when("I send a {string} request to {string}") + public async sendARequestWithBodyToWith(method: string, path: string): Promise { + this.response = await this.axios.request({ + method: method, + baseURL: `http://${process.env.WEB_HOST}:${process.env.WEB_PORT}`, + url: path + }) + } + + @then("the response status should be {int} with body:") + public async theResponseStatusAndBodyShouldBe(status: number, body: string): Promise { + assert.equal(this.response?.status, status) + assert.equal(JSON.stringify(this.response?.data), JSON.stringify(JSON.parse(body))) + } +} + +export default HttpSteps