Browse Source

debugging

master
Christof Nolle 1 year ago
parent
commit
fe9d2aef24

+ 1
- 1
src/main/scala/de/codingchallenge/Main.scala View File

@@ -51,7 +51,7 @@ object Main extends App with DependencyInjectionModule with LazyLogging {

bindingFuture.onComplete {
case Success(_) =>
println(s"Server for started at localhost:8080")
println(s"Server for started at localhost:9080")
case Failure(cause) =>
println(s"Server failed to start", cause)
}

+ 5
- 5
src/main/scala/de/codingchallenge/Routes.scala View File

@@ -4,7 +4,7 @@ import akka.actor.ActorSystem
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives.{pathPrefix, _}
import akka.http.scaladsl.server._
import akka.stream.ActorMaterializer
import akka.stream.{ActorMaterializer, Materializer}
import akka.util.ByteString
import de.codingchallenge.configuration.Environment
import com.typesafe.scalalogging.LazyLogging
@@ -16,8 +16,8 @@ class Routes(articleExportService: ArticleExportService,
environment: Environment)
extends LazyLogging {

implicit val ec = actorSystem
implicit val amaterializer = actorMaterializer
implicit val as: ActorSystem = actorSystem
implicit val mat: Materializer = actorMaterializer

var on: Boolean = true

@@ -44,8 +44,8 @@ class Routes(articleExportService: ArticleExportService,
val exportRoute: Route =
path("articles") {
get {
onSuccess(articleExportService.exportArticles()) { _ =>
complete(StatusCodes.NoContent)
onSuccess(articleExportService.exportArticles()) { res =>
complete(res)
}
}
}

+ 17
- 3
src/main/scala/de/codingchallenge/models/ProductExport.scala View File

@@ -1,13 +1,27 @@
package de.codingchallenge.models

import java.text.DecimalFormat
import java.util.Locale

import de.codingchallenge.csv.CsvColumnWrites

/**
* Data structure representing the CSV export
* @param productId product group identifier
* @param name name of the product
* @param description description of the product
* @param price price
* @param stockSum stock over group
*/
case class ProductExport(productId: String, name: String, description: String, price:Float, stockSum: Int)

object ProductExport{
object ProductExport {


implicit val columnWrites: CsvColumnWrites[ProductExport] = (p: ProductExport) => Seq(
p.productId, p.name, p.description,"%.2f".formatLocal(java.util.Locale.US, p.price), p.stockSum.toString
)

implicit val columnWrites: CsvColumnWrites[ProductExport] = (p: ProductExport) =>
p.productIterator.map(_.toString).toSeq

def apply(a: Article, stockSum: Int): ProductExport = new ProductExport(
productId = a.productId,

+ 10
- 11
src/main/scala/de/codingchallenge/repositories/ProductExportRepository.scala View File

@@ -3,9 +3,9 @@ package de.codingchallenge.repositories
import akka.NotUsed
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpEntity.Chunked
import akka.http.scaladsl.model._
import akka.stream.scaladsl.{Flow, Source}
import akka.stream.{ActorMaterializer, Materializer}
import akka.util.ByteString
import com.typesafe.scalalogging.LazyLogging
import de.codingchallenge.csv.CsvOps._
@@ -16,6 +16,7 @@ import scala.concurrent.{ExecutionContext, Future}
class ProductExportRepository(actorSystem: ActorSystem)(implicit ec: ExecutionContext) extends LazyLogging {

private implicit val as: ActorSystem = actorSystem
private implicit val mat: Materializer = ActorMaterializer()

val headerLine = "produktId|name|beschreibung|preis|summeBestand"

@@ -28,27 +29,25 @@ class ProductExportRepository(actorSystem: ActorSystem)(implicit ec: ExecutionCo
.intersperse("\n")
.map(ByteString.apply)

// it did not work with charset information
val entity = Chunked.fromData(
ContentType.WithMissingCharset(MediaTypes.`text/csv`),
sourceWithHeader
)

/**
* Setting charset to utf8 results in HTTP.406
*/
Http()
.singleRequest(
HttpRequest(
method = HttpMethods.PUT,
uri = s"$baseUrl/products/$articlesSize",
entity = entity))
.map{res =>
entity = HttpEntity(ContentType.WithMissingCharset(MediaTypes.`text/csv`), sourceWithHeader)
))
.map { res =>
logger.info(s"Server responded with $res")
res
}
}

private val csvFlow: Flow[ProductExport, String, NotUsed] =
Flow.fromFunction{p =>
logger.info(s"processing export record $p")
Flow.fromFunction { p =>
logger.info(s"streaming export record $p")
p.toCsvLine
}


+ 10
- 6
src/main/scala/de/codingchallenge/services/ArticleExportService.scala View File

@@ -18,7 +18,7 @@ class ArticleExportService(
) extends LazyLogging {
implicit val m: Materializer = mat

val productsSize: Int = 100
val productsSize: Int = 20

/**
* Streams articles to generate a product export.
@@ -34,16 +34,20 @@ class ArticleExportService(
def exportArticles(): Future[HttpResponse] = productExportRepository.add(Source.fromGraph[ProductExport, NotUsed](
articleRepository
.getArticles(productsSize)
.filter(_.stock > 0)
// .filter(_.stock > 0)
.map(a => (a, a.stock))
.via(new AccumulateWhileUnchanged[(Article, Int), String](_._1.productId))
.map(_.reduce[(Article, Int)] {
case ((a1, c1), (a2, c2)) if a1.price > a2.price => (a2, c1 + c2)
case ((a1, c1), (_, c2)) => (a1, c1 + c2)
})
.map(reduceTuples)
.map { case (article, stockSum) =>
logger.info(s"Reduced to article: $article and stockSum: $stockSum")
ProductExport(article, stockSum) }
), productsSize)

def reduceTuples(t: Seq[(Article, Int)]): (Article, Int) = {
logger.info(s"reducing group $t")
t.reduceLeft[(Article, Int)] {
case ((a1, c1), (a2, c2)) if a1.price > a2.price => (a2, c1 + c2)
case ((a1, c1), (_, c2)) => (a1, c1 + c2)
}
}
}

+ 10
- 0
src/test/scala/de/codingchallenge/fixtures/ArticleFixture.scala View File

@@ -123,4 +123,14 @@ trait ArticleFixture {
A-BdclQqXJ|P-BdclQqXJ|J YSQPS||22.28|49
A-OHAeNBak|P-BdclQqXJ|JWWJVYSQPS C TRYDYX|pvAlqsw bnln ijxmdog roqumthtp lijqubnzz|97.32|24
A-dclQqXJ7|P-BdclQqXJ|QPSU||77.34|17""".split("\n").map(_.trim)

val sampleGroup = Seq(
"A-C7PeyUjD|P-C7PeyUjD|MGT|swwfkg rzDawtwl vvvjebHd aenb aXkcwJyqsc|77.34|17",
"A-LPeyUjDg|P-C7PeyUjD|BMSNWL HFH||45.85|0",
"A-7PeyUjDg|P-C7PeyUjD|BMSNWL HFH||45.85|0",
"A-peyUjDgF|P-C7PeyUjD|SN L|hkqrz u wtwllbkxkl Hdwcpl|87.72|1",
"A-DYMxSQVE|P-C7PeyUjD|GTFB|wwfkgu zDawtw|84.12|7",
"A-PeyUjDgF|P-C7PeyUjD|SN L|hkqrz u wtwllbkxkl Hdwcpl|87.72|1"
)

}

+ 26
- 4
src/test/scala/de/codingchallenge/services/ArticleExportServiceSpec.scala View File

@@ -49,7 +49,7 @@ class ArticleExportServiceSpec extends BaseSpec with AkkaSpec{
verify(productExportRepositoryMock).add(sourceCaptor.capture(), any())
Await.result(sourceCaptor.getValue.runWith(Sink.head), 1.second) mustBe productExport
}
"pass sample data as expected" in new TestSetup with ArticleFixture {
"contain only one entry per product" in new TestSetup with ArticleFixture {

val articles = sampleData.map(_.csvToOptOf[Article].get).toList
doReturn(Source(articles), Nil: _*)
@@ -61,9 +61,31 @@ class ArticleExportServiceSpec extends BaseSpec with AkkaSpec{
Await.result(service.exportArticles(), 5.second)
val sourceCaptor: ArgumentCaptor[Source[ProductExport, NotUsed]] = ArgumentCaptor.forClass(classOf[Source[ProductExport, NotUsed]])
verify(productExportRepositoryMock).add(sourceCaptor.capture(), any())
Await.result(sourceCaptor.getValue.runWith(Sink.seq), 1.second).foreach { e =>
e mustBe a[ProductExport]
}
val results = Await.result(sourceCaptor.getValue.runWith(Sink.seq), 1.second)
results.foreach{
r => results.count(_.productId == r.productId) == 1
}
}
"find the cheapest element of a group" in new TestSetup with ArticleFixture {

val articles = sampleGroup.map(_.csvToOptOf[Article].get).toList
doReturn(Source(articles), Nil: _*)
.when(articleRepositoryMock)
.getArticles(100)
when(productExportRepositoryMock.add(any(), any()))
.thenReturn(Future.successful(HttpResponse()))

Await.result(service.exportArticles(), 5.second)
val sourceCaptor: ArgumentCaptor[Source[ProductExport, NotUsed]] = ArgumentCaptor.forClass(classOf[Source[ProductExport, NotUsed]])
verify(productExportRepositoryMock).add(sourceCaptor.capture(), any())
val results = Await.result(sourceCaptor.getValue.runWith(Sink.seq), 1.second)
results.head mustBe ProductExport(
productId = "P-C7PeyUjD",
name = "BMSNWL HFH",
description = "",
price = 45.85f,
stockSum = 26
)
}
}
}

Loading…
Cancel
Save