From d923c62a554ee7cbd79bc8461b3526a16dc69254 Mon Sep 17 00:00:00 2001 From: Krzysztof Borowski Date: Tue, 19 Jan 2016 13:52:03 +0100 Subject: [PATCH 1/8] fix tests #19 --- .travis.yml | 1 - .../scala/org/virtuslab/beholder/JsonFiltersTests.scala | 4 ++-- .../org/virtuslab/beholder/json/JsonFormatterTest.scala | 6 ++---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1cfce33..76b760f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: scala scala: - "2.11.5" jdk: - - openjdk7 - oraclejdk8 script: sbt -no-colors ++$TRAVIS_SCALA_VERSION clean coverage test \ No newline at end of file diff --git a/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala b/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala index 2a7b2d1..c42d02e 100644 --- a/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala +++ b/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala @@ -7,7 +7,7 @@ import org.virtuslab.beholder.filters.json.{ JsonFilterFields, JsonFilters, Json import org.virtuslab.beholder.filters.{ FilterAPI, FilterDefinition } import org.virtuslab.beholder.suites.{ InitialQueryTestSuite, BaseSuite, FiltersTestSuite, RangeFiltersSuite } import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ -import play.api.libs.json.JsObject +import play.api.libs.json.{ JsSuccess, JsObject } trait JsonFiltersTestsBase { self: AppTest with BaseSuite[JsonFormatter[UserMachineViewRow]] => @@ -18,7 +18,7 @@ trait JsonFiltersTestsBase { filter.formatter.results(currentFilter, result) match { case JsObject(Seq(("filter", jsonFilter), _)) => - filter.formatter.filterDefinition(jsonFilter) should equal(Some(currentFilter)) + filter.formatter.filterDefinition(jsonFilter) should equal(JsSuccess(currentFilter)) } result.content diff --git a/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala b/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala index 3ca2362..48c8428 100644 --- a/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala +++ b/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala @@ -7,7 +7,7 @@ import org.virtuslab.beholder.filters.json.JsonFilterFields._ import org.virtuslab.beholder.filters.json.{ JsonFilterFields, JsonFilters } import org.virtuslab.beholder.{ UserMachineViewRow, _ } import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ -import play.api.libs.json.{ JsArray, JsObject, JsString } +import play.api.libs.json.{ JsSuccess, JsArray, JsObject, JsString } class JsonFormatterTest extends AppTest with UserMachinesView with ModelIncluded { @@ -34,7 +34,7 @@ class JsonFormatterTest extends AppTest with UserMachinesView with ModelIncluded val data = FilterDefinition(None, None, None, Seq(Some("ala"), None, None, None, None)) - filter.formatter.filterDefinition(req) shouldEqual Some(data) + filter.formatter.filterDefinition(req) shouldEqual JsSuccess(data) } it should "create json definition correctly" in rollbackWithModel { @@ -44,8 +44,6 @@ class JsonFormatterTest extends AppTest with UserMachinesView with ModelIncluded val definition = filter.formatter.jsonDefinition - println(definition) - def stringValue(on: JsObject, name: String) = on \ name match { case JsString(value) => value case _ => fail(s"Field $name fro $on is not string!") From 3226f348b8eafe68ca6afe990a1058be62ea98f6 Mon Sep 17 00:00:00 2001 From: Jan Paw Date: Thu, 17 Dec 2015 13:56:56 +0100 Subject: [PATCH 2/8] support up to 22 columns --- README.md | 1 + build.sbt | 3 +- .../filters/FilterFactoryMethods.scala | 154 ++++++++ .../virtuslab/beholder/views/BaseView.scala | 2 +- .../views/FilterableViewsGenerateCode.scala | 348 ++++++++++++++++++ 5 files changed, 506 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0425a1c..b090a37 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Authors: * [Krzysztof Romanowski](https://github.com/romanowski) * [Jerzy Müller](https://github.com/Kwestor) * [Mikołaj Jakubowski](https://github.com/mkljakubowski) +* [Jan Paw](https://github.com/explicite) * [Krzysztof Borowski](https://github.com/liosedhel) Feel free to use it, test it and to contribute! diff --git a/build.sbt b/build.sbt index 03226b1..f7186ef 100644 --- a/build.sbt +++ b/build.sbt @@ -77,5 +77,6 @@ ScoverageSbtPlugin.ScoverageKeys.coverageExcludedPackages := Seq( "org.virtuslab.beholder.utils.generators.*", // only BaseView5 is tested, all are generated, so there is no need to check them all "org.virtuslab.beholder.views.FilterableViews.*", - "org.virtuslab.beholder.views.FilterableViewsGenerateCode.BaseView[^5].*" + "org.virtuslab.beholder.views.FilterableViewsGenerateCode.BaseView[^5].*", + "org.virtuslab.beholder.filters.FilterFactoryMethods.*" ).mkString(";") diff --git a/src/main/scala/org/virtuslab/beholder/filters/FilterFactoryMethods.scala b/src/main/scala/org/virtuslab/beholder/filters/FilterFactoryMethods.scala index 188db0e..28a5c71 100644 --- a/src/main/scala/org/virtuslab/beholder/filters/FilterFactoryMethods.scala +++ b/src/main/scala/org/virtuslab/beholder/filters/FilterFactoryMethods.scala @@ -468,4 +468,158 @@ abstract class FilterFactoryMethods[Entity, FieldType[_, _] <: MappedFilterField } } + def create[A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, T <: BaseView19[Entity, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19]]( + table: TableQuery[T], + c1Mapping: FieldType[A1, B1], + c2Mapping: FieldType[A2, B2], + c3Mapping: FieldType[A3, B3], + c4Mapping: FieldType[A4, B4], + c5Mapping: FieldType[A5, B5], + c6Mapping: FieldType[A6, B6], + c7Mapping: FieldType[A7, B7], + c8Mapping: FieldType[A8, B8], + c9Mapping: FieldType[A9, B9], + c10Mapping: FieldType[A10, B10], + c11Mapping: FieldType[A11, B11], + c12Mapping: FieldType[A12, B12], + c13Mapping: FieldType[A13, B13], + c14Mapping: FieldType[A14, B14], + c15Mapping: FieldType[A15, B15], + c16Mapping: FieldType[A16, B16], + c17Mapping: FieldType[A17, B17], + c18Mapping: FieldType[A18, B18], + c19Mapping: FieldType[A19, B19] + ): TableFilterAPI[Entity, Formatter, T] = { + + new BaseFilter[A1, Entity, T, FieldType[_, _], Formatter](table) { + override val formatter: Formatter = createFormatter(this) + + override protected def emptyFilterDataInner: Seq[Option[Any]] = Seq.fill(19)(None) + + override def filterFields: Seq[FieldType[_, _]] = + Seq[FieldType[_, _]](c1Mapping, c2Mapping, c3Mapping, c4Mapping, c5Mapping, c6Mapping, c7Mapping, c8Mapping, c9Mapping, c10Mapping, c11Mapping, c12Mapping, c13Mapping, c14Mapping, c15Mapping, c16Mapping, c17Mapping, c18Mapping, c19Mapping) + + override protected def tableColumns(table: T): Seq[LongUnicornPlay.driver.simple.Column[_]] = Seq( + table.c1, table.c2, table.c3, table.c4, table.c5, table.c6, table.c7, table.c8, table.c9, table.c10, table.c11, table.c12, table.c13, table.c14, table.c15, table.c16, table.c17, table.c18, table.c19 + ) + } + } + + def create[A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, B20, T <: BaseView20[Entity, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20]]( + table: TableQuery[T], + c1Mapping: FieldType[A1, B1], + c2Mapping: FieldType[A2, B2], + c3Mapping: FieldType[A3, B3], + c4Mapping: FieldType[A4, B4], + c5Mapping: FieldType[A5, B5], + c6Mapping: FieldType[A6, B6], + c7Mapping: FieldType[A7, B7], + c8Mapping: FieldType[A8, B8], + c9Mapping: FieldType[A9, B9], + c10Mapping: FieldType[A10, B10], + c11Mapping: FieldType[A11, B11], + c12Mapping: FieldType[A12, B12], + c13Mapping: FieldType[A13, B13], + c14Mapping: FieldType[A14, B14], + c15Mapping: FieldType[A15, B15], + c16Mapping: FieldType[A16, B16], + c17Mapping: FieldType[A17, B17], + c18Mapping: FieldType[A18, B18], + c19Mapping: FieldType[A19, B19], + c20Mapping: FieldType[A20, B20] + ): TableFilterAPI[Entity, Formatter, T] = { + + new BaseFilter[A1, Entity, T, FieldType[_, _], Formatter](table) { + override val formatter: Formatter = createFormatter(this) + + override protected def emptyFilterDataInner: Seq[Option[Any]] = Seq.fill(20)(None) + + override def filterFields: Seq[FieldType[_, _]] = + Seq[FieldType[_, _]](c1Mapping, c2Mapping, c3Mapping, c4Mapping, c5Mapping, c6Mapping, c7Mapping, c8Mapping, c9Mapping, c10Mapping, c11Mapping, c12Mapping, c13Mapping, c14Mapping, c15Mapping, c16Mapping, c17Mapping, c18Mapping, c19Mapping, c20Mapping) + + override protected def tableColumns(table: T): Seq[LongUnicornPlay.driver.simple.Column[_]] = Seq( + table.c1, table.c2, table.c3, table.c4, table.c5, table.c6, table.c7, table.c8, table.c9, table.c10, table.c11, table.c12, table.c13, table.c14, table.c15, table.c16, table.c17, table.c18, table.c19, table.c20 + ) + } + } + + def create[A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType, A21: TypedType, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, B20, B21, T <: BaseView21[Entity, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21]]( + table: TableQuery[T], + c1Mapping: FieldType[A1, B1], + c2Mapping: FieldType[A2, B2], + c3Mapping: FieldType[A3, B3], + c4Mapping: FieldType[A4, B4], + c5Mapping: FieldType[A5, B5], + c6Mapping: FieldType[A6, B6], + c7Mapping: FieldType[A7, B7], + c8Mapping: FieldType[A8, B8], + c9Mapping: FieldType[A9, B9], + c10Mapping: FieldType[A10, B10], + c11Mapping: FieldType[A11, B11], + c12Mapping: FieldType[A12, B12], + c13Mapping: FieldType[A13, B13], + c14Mapping: FieldType[A14, B14], + c15Mapping: FieldType[A15, B15], + c16Mapping: FieldType[A16, B16], + c17Mapping: FieldType[A17, B17], + c18Mapping: FieldType[A18, B18], + c19Mapping: FieldType[A19, B19], + c20Mapping: FieldType[A20, B20], + c21Mapping: FieldType[A21, B21] + ): TableFilterAPI[Entity, Formatter, T] = { + + new BaseFilter[A1, Entity, T, FieldType[_, _], Formatter](table) { + override val formatter: Formatter = createFormatter(this) + + override protected def emptyFilterDataInner: Seq[Option[Any]] = Seq.fill(21)(None) + + override def filterFields: Seq[FieldType[_, _]] = + Seq[FieldType[_, _]](c1Mapping, c2Mapping, c3Mapping, c4Mapping, c5Mapping, c6Mapping, c7Mapping, c8Mapping, c9Mapping, c10Mapping, c11Mapping, c12Mapping, c13Mapping, c14Mapping, c15Mapping, c16Mapping, c17Mapping, c18Mapping, c19Mapping, c20Mapping, c21Mapping) + + override protected def tableColumns(table: T): Seq[LongUnicornPlay.driver.simple.Column[_]] = Seq( + table.c1, table.c2, table.c3, table.c4, table.c5, table.c6, table.c7, table.c8, table.c9, table.c10, table.c11, table.c12, table.c13, table.c14, table.c15, table.c16, table.c17, table.c18, table.c19, table.c20, table.c21 + ) + } + } + + def create[A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType, A21: TypedType, A22: TypedType, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, B20, B21, B22, T <: BaseView22[Entity, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22]]( + table: TableQuery[T], + c1Mapping: FieldType[A1, B1], + c2Mapping: FieldType[A2, B2], + c3Mapping: FieldType[A3, B3], + c4Mapping: FieldType[A4, B4], + c5Mapping: FieldType[A5, B5], + c6Mapping: FieldType[A6, B6], + c7Mapping: FieldType[A7, B7], + c8Mapping: FieldType[A8, B8], + c9Mapping: FieldType[A9, B9], + c10Mapping: FieldType[A10, B10], + c11Mapping: FieldType[A11, B11], + c12Mapping: FieldType[A12, B12], + c13Mapping: FieldType[A13, B13], + c14Mapping: FieldType[A14, B14], + c15Mapping: FieldType[A15, B15], + c16Mapping: FieldType[A16, B16], + c17Mapping: FieldType[A17, B17], + c18Mapping: FieldType[A18, B18], + c19Mapping: FieldType[A19, B19], + c20Mapping: FieldType[A20, B20], + c21Mapping: FieldType[A21, B21], + c22Mapping: FieldType[A22, B22] + ): TableFilterAPI[Entity, Formatter, T] = { + + new BaseFilter[A1, Entity, T, FieldType[_, _], Formatter](table) { + override val formatter: Formatter = createFormatter(this) + + override protected def emptyFilterDataInner: Seq[Option[Any]] = Seq.fill(22)(None) + + override def filterFields: Seq[FieldType[_, _]] = + Seq[FieldType[_, _]](c1Mapping, c2Mapping, c3Mapping, c4Mapping, c5Mapping, c6Mapping, c7Mapping, c8Mapping, c9Mapping, c10Mapping, c11Mapping, c12Mapping, c13Mapping, c14Mapping, c15Mapping, c16Mapping, c17Mapping, c18Mapping, c19Mapping, c20Mapping, c21Mapping, c22Mapping) + + override protected def tableColumns(table: T): Seq[LongUnicornPlay.driver.simple.Column[_]] = Seq( + table.c1, table.c2, table.c3, table.c4, table.c5, table.c6, table.c7, table.c8, table.c9, table.c10, table.c11, table.c12, table.c13, table.c14, table.c15, table.c16, table.c17, table.c18, table.c19, table.c20, table.c21, table.c22 + ) + } + } + } diff --git a/src/main/scala/org/virtuslab/beholder/views/BaseView.scala b/src/main/scala/org/virtuslab/beholder/views/BaseView.scala index f70e3e4..facd0e8 100644 --- a/src/main/scala/org/virtuslab/beholder/views/BaseView.scala +++ b/src/main/scala/org/virtuslab/beholder/views/BaseView.scala @@ -44,7 +44,7 @@ abstract class BaseView[Id, Entity](tag: Tag, val viewName: String) extends Base object BaseView { implicit class WithViewDDL(val query: TableQuery[_ <: BaseView[_, _]]) extends AnyVal { - def viewDDL = ViewDDL(query.shaped.value) + def viewDDL = ViewDDL(query.baseTableRow) } case class ViewDDL(table: BaseView[_, _]) extends DDL { diff --git a/src/main/scala/org/virtuslab/beholder/views/FilterableViewsGenerateCode.scala b/src/main/scala/org/virtuslab/beholder/views/FilterableViewsGenerateCode.scala index 98b6fdc..84801c4 100644 --- a/src/main/scala/org/virtuslab/beholder/views/FilterableViewsGenerateCode.scala +++ b/src/main/scala/org/virtuslab/beholder/views/FilterableViewsGenerateCode.scala @@ -1083,4 +1083,352 @@ private[beholder] trait FilterableViewsGenerateCode { def * = (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18) <> (apply.tupled, unapply) } + def createView[T: ClassTag, E, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType]( + name: String, + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19)], + baseQuery: Query[E, _, Seq] + )( + mappings: E => ((String, Column[A1]), (String, Column[A2]), (String, Column[A3]), (String, Column[A4]), (String, Column[A5]), (String, Column[A6]), (String, Column[A7]), (String, Column[A8]), (String, Column[A9]), (String, Column[A10]), (String, Column[A11]), (String, Column[A12]), (String, Column[A13]), (String, Column[A14]), (String, Column[A15]), (String, Column[A16]), (String, Column[A17]), (String, Column[A18]), (String, Column[A19])) + ): TableQuery[BaseView19[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19]] = { + + var columnsNames = Seq[String]() + + val preparedQuery: Query[_, T, Seq] = { + val mappedQuery = baseQuery.map { + t => + mappings(t) match { + case ((name1, c1), (name2, c2), (name3, c3), (name4, c4), (name5, c5), (name6, c6), (name7, c7), (name8, c8), (name9, c9), (name10, c10), (name11, c11), (name12, c12), (name13, c13), (name14, c14), (name15, c15), (name16, c16), (name17, c17), (name18, c18), (name19, c19)) => + columnsNames = Seq(name1, name2, name3, name4, name5, name6, name7, name8, name9, name10, name11, name12, name13, name14, name15, name16, name17, name18, name19) + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19) <> (apply.tupled, unapply) + } + } + + for { + a <- mappedQuery + } yield a + } + TableQuery.apply(tag => new BaseView19[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19](tag, name, columnsNames, apply, unapply, preparedQuery)) + + } + + class BaseView19[T: ClassTag, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType]( + tag: Tag, + name: String, + val columnNames: Seq[String], + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19)], + val query: Query[_, T, Seq] + ) extends BaseView[A1, T](tag, name) { + def c1 = column[A1](columnNames(0)) + def c2 = column[A2](columnNames(1)) + def c3 = column[A3](columnNames(2)) + def c4 = column[A4](columnNames(3)) + def c5 = column[A5](columnNames(4)) + def c6 = column[A6](columnNames(5)) + def c7 = column[A7](columnNames(6)) + def c8 = column[A8](columnNames(7)) + def c9 = column[A9](columnNames(8)) + def c10 = column[A10](columnNames(9)) + def c11 = column[A11](columnNames(10)) + def c12 = column[A12](columnNames(11)) + def c13 = column[A13](columnNames(12)) + def c14 = column[A14](columnNames(13)) + def c15 = column[A15](columnNames(14)) + def c16 = column[A16](columnNames(15)) + def c17 = column[A17](columnNames(16)) + def c18 = column[A18](columnNames(17)) + def c19 = column[A19](columnNames(18)) + + override def id = c1 + + override protected val columns: Seq[(String, this.type => Column[_])] = Seq( + columnNames(0) -> (_.c1), + columnNames(1) -> (_.c2), + columnNames(2) -> (_.c3), + columnNames(3) -> (_.c4), + columnNames(4) -> (_.c5), + columnNames(5) -> (_.c6), + columnNames(6) -> (_.c7), + columnNames(7) -> (_.c8), + columnNames(8) -> (_.c9), + columnNames(9) -> (_.c10), + columnNames(10) -> (_.c11), + columnNames(11) -> (_.c12), + columnNames(12) -> (_.c13), + columnNames(13) -> (_.c14), + columnNames(14) -> (_.c15), + columnNames(15) -> (_.c16), + columnNames(16) -> (_.c17), + columnNames(17) -> (_.c18), + columnNames(18) -> (_.c19) + ) + + def * = (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19) <> (apply.tupled, unapply) + } + + def createView[T: ClassTag, E, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType]( + name: String, + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20)], + baseQuery: Query[E, _, Seq] + )( + mappings: E => ((String, Column[A1]), (String, Column[A2]), (String, Column[A3]), (String, Column[A4]), (String, Column[A5]), (String, Column[A6]), (String, Column[A7]), (String, Column[A8]), (String, Column[A9]), (String, Column[A10]), (String, Column[A11]), (String, Column[A12]), (String, Column[A13]), (String, Column[A14]), (String, Column[A15]), (String, Column[A16]), (String, Column[A17]), (String, Column[A18]), (String, Column[A19]), (String, Column[A20])) + ): TableQuery[BaseView20[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20]] = { + + var columnsNames = Seq[String]() + + val preparedQuery: Query[_, T, Seq] = { + val mappedQuery = baseQuery.map { + t => + mappings(t) match { + case ((name1, c1), (name2, c2), (name3, c3), (name4, c4), (name5, c5), (name6, c6), (name7, c7), (name8, c8), (name9, c9), (name10, c10), (name11, c11), (name12, c12), (name13, c13), (name14, c14), (name15, c15), (name16, c16), (name17, c17), (name18, c18), (name19, c19), (name20, c20)) => + columnsNames = Seq(name1, name2, name3, name4, name5, name6, name7, name8, name9, name10, name11, name12, name13, name14, name15, name16, name17, name18, name19, name20) + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20) <> (apply.tupled, unapply) + } + } + + for { + a <- mappedQuery + } yield a + } + TableQuery.apply(tag => new BaseView20[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20](tag, name, columnsNames, apply, unapply, preparedQuery)) + + } + + class BaseView20[T: ClassTag, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType]( + tag: Tag, + name: String, + val columnNames: Seq[String], + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20)], + val query: Query[_, T, Seq] + ) extends BaseView[A1, T](tag, name) { + def c1 = column[A1](columnNames(0)) + def c2 = column[A2](columnNames(1)) + def c3 = column[A3](columnNames(2)) + def c4 = column[A4](columnNames(3)) + def c5 = column[A5](columnNames(4)) + def c6 = column[A6](columnNames(5)) + def c7 = column[A7](columnNames(6)) + def c8 = column[A8](columnNames(7)) + def c9 = column[A9](columnNames(8)) + def c10 = column[A10](columnNames(9)) + def c11 = column[A11](columnNames(10)) + def c12 = column[A12](columnNames(11)) + def c13 = column[A13](columnNames(12)) + def c14 = column[A14](columnNames(13)) + def c15 = column[A15](columnNames(14)) + def c16 = column[A16](columnNames(15)) + def c17 = column[A17](columnNames(16)) + def c18 = column[A18](columnNames(17)) + def c19 = column[A19](columnNames(18)) + def c20 = column[A20](columnNames(19)) + + override def id = c1 + + override protected val columns: Seq[(String, this.type => Column[_])] = Seq( + columnNames(0) -> (_.c1), + columnNames(1) -> (_.c2), + columnNames(2) -> (_.c3), + columnNames(3) -> (_.c4), + columnNames(4) -> (_.c5), + columnNames(5) -> (_.c6), + columnNames(6) -> (_.c7), + columnNames(7) -> (_.c8), + columnNames(8) -> (_.c9), + columnNames(9) -> (_.c10), + columnNames(10) -> (_.c11), + columnNames(11) -> (_.c12), + columnNames(12) -> (_.c13), + columnNames(13) -> (_.c14), + columnNames(14) -> (_.c15), + columnNames(15) -> (_.c16), + columnNames(16) -> (_.c17), + columnNames(17) -> (_.c18), + columnNames(18) -> (_.c19), + columnNames(19) -> (_.c20) + ) + + def * = (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20) <> (apply.tupled, unapply) + } + + def createView[T: ClassTag, E, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType, A21: TypedType]( + name: String, + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21)], + baseQuery: Query[E, _, Seq] + )( + mappings: E => ((String, Column[A1]), (String, Column[A2]), (String, Column[A3]), (String, Column[A4]), (String, Column[A5]), (String, Column[A6]), (String, Column[A7]), (String, Column[A8]), (String, Column[A9]), (String, Column[A10]), (String, Column[A11]), (String, Column[A12]), (String, Column[A13]), (String, Column[A14]), (String, Column[A15]), (String, Column[A16]), (String, Column[A17]), (String, Column[A18]), (String, Column[A19]), (String, Column[A20]), (String, Column[A21])) + ): TableQuery[BaseView21[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21]] = { + + var columnsNames = Seq[String]() + + val preparedQuery: Query[_, T, Seq] = { + val mappedQuery = baseQuery.map { + t => + mappings(t) match { + case ((name1, c1), (name2, c2), (name3, c3), (name4, c4), (name5, c5), (name6, c6), (name7, c7), (name8, c8), (name9, c9), (name10, c10), (name11, c11), (name12, c12), (name13, c13), (name14, c14), (name15, c15), (name16, c16), (name17, c17), (name18, c18), (name19, c19), (name20, c20), (name21, c21)) => + columnsNames = Seq(name1, name2, name3, name4, name5, name6, name7, name8, name9, name10, name11, name12, name13, name14, name15, name16, name17, name18, name19, name20, name21) + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21) <> (apply.tupled, unapply) + } + } + + for { + a <- mappedQuery + } yield a + } + TableQuery.apply(tag => new BaseView21[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21](tag, name, columnsNames, apply, unapply, preparedQuery)) + + } + + class BaseView21[T: ClassTag, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType, A21: TypedType]( + tag: Tag, + name: String, + val columnNames: Seq[String], + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21)], + val query: Query[_, T, Seq] + ) extends BaseView[A1, T](tag, name) { + def c1 = column[A1](columnNames(0)) + def c2 = column[A2](columnNames(1)) + def c3 = column[A3](columnNames(2)) + def c4 = column[A4](columnNames(3)) + def c5 = column[A5](columnNames(4)) + def c6 = column[A6](columnNames(5)) + def c7 = column[A7](columnNames(6)) + def c8 = column[A8](columnNames(7)) + def c9 = column[A9](columnNames(8)) + def c10 = column[A10](columnNames(9)) + def c11 = column[A11](columnNames(10)) + def c12 = column[A12](columnNames(11)) + def c13 = column[A13](columnNames(12)) + def c14 = column[A14](columnNames(13)) + def c15 = column[A15](columnNames(14)) + def c16 = column[A16](columnNames(15)) + def c17 = column[A17](columnNames(16)) + def c18 = column[A18](columnNames(17)) + def c19 = column[A19](columnNames(18)) + def c20 = column[A20](columnNames(19)) + def c21 = column[A21](columnNames(20)) + + override def id = c1 + + override protected val columns: Seq[(String, this.type => Column[_])] = Seq( + columnNames(0) -> (_.c1), + columnNames(1) -> (_.c2), + columnNames(2) -> (_.c3), + columnNames(3) -> (_.c4), + columnNames(4) -> (_.c5), + columnNames(5) -> (_.c6), + columnNames(6) -> (_.c7), + columnNames(7) -> (_.c8), + columnNames(8) -> (_.c9), + columnNames(9) -> (_.c10), + columnNames(10) -> (_.c11), + columnNames(11) -> (_.c12), + columnNames(12) -> (_.c13), + columnNames(13) -> (_.c14), + columnNames(14) -> (_.c15), + columnNames(15) -> (_.c16), + columnNames(16) -> (_.c17), + columnNames(17) -> (_.c18), + columnNames(18) -> (_.c19), + columnNames(19) -> (_.c20), + columnNames(20) -> (_.c21) + ) + + def * = (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21) <> (apply.tupled, unapply) + } + + def createView[T: ClassTag, E, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType, A21: TypedType, A22: TypedType]( + name: String, + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22)], + baseQuery: Query[E, _, Seq] + )( + mappings: E => ((String, Column[A1]), (String, Column[A2]), (String, Column[A3]), (String, Column[A4]), (String, Column[A5]), (String, Column[A6]), (String, Column[A7]), (String, Column[A8]), (String, Column[A9]), (String, Column[A10]), (String, Column[A11]), (String, Column[A12]), (String, Column[A13]), (String, Column[A14]), (String, Column[A15]), (String, Column[A16]), (String, Column[A17]), (String, Column[A18]), (String, Column[A19]), (String, Column[A20]), (String, Column[A21]), (String, Column[A22])) + ): TableQuery[BaseView22[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22]] = { + + var columnsNames = Seq[String]() + + val preparedQuery: Query[_, T, Seq] = { + val mappedQuery = baseQuery.map { + t => + mappings(t) match { + case ((name1, c1), (name2, c2), (name3, c3), (name4, c4), (name5, c5), (name6, c6), (name7, c7), (name8, c8), (name9, c9), (name10, c10), (name11, c11), (name12, c12), (name13, c13), (name14, c14), (name15, c15), (name16, c16), (name17, c17), (name18, c18), (name19, c19), (name20, c20), (name21, c21), (name22, c22)) => + columnsNames = Seq(name1, name2, name3, name4, name5, name6, name7, name8, name9, name10, name11, name12, name13, name14, name15, name16, name17, name18, name19, name20, name21, name22) + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22) <> (apply.tupled, unapply) + } + } + + for { + a <- mappedQuery + } yield a + } + TableQuery.apply(tag => new BaseView22[T, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22](tag, name, columnsNames, apply, unapply, preparedQuery)) + + } + + class BaseView22[T: ClassTag, A1: TypedType, A2: TypedType, A3: TypedType, A4: TypedType, A5: TypedType, A6: TypedType, A7: TypedType, A8: TypedType, A9: TypedType, A10: TypedType, A11: TypedType, A12: TypedType, A13: TypedType, A14: TypedType, A15: TypedType, A16: TypedType, A17: TypedType, A18: TypedType, A19: TypedType, A20: TypedType, A21: TypedType, A22: TypedType]( + tag: Tag, + name: String, + val columnNames: Seq[String], + apply: (A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22) => T, + unapply: T => Option[(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22)], + val query: Query[_, T, Seq] + ) extends BaseView[A1, T](tag, name) { + def c1 = column[A1](columnNames(0)) + def c2 = column[A2](columnNames(1)) + def c3 = column[A3](columnNames(2)) + def c4 = column[A4](columnNames(3)) + def c5 = column[A5](columnNames(4)) + def c6 = column[A6](columnNames(5)) + def c7 = column[A7](columnNames(6)) + def c8 = column[A8](columnNames(7)) + def c9 = column[A9](columnNames(8)) + def c10 = column[A10](columnNames(9)) + def c11 = column[A11](columnNames(10)) + def c12 = column[A12](columnNames(11)) + def c13 = column[A13](columnNames(12)) + def c14 = column[A14](columnNames(13)) + def c15 = column[A15](columnNames(14)) + def c16 = column[A16](columnNames(15)) + def c17 = column[A17](columnNames(16)) + def c18 = column[A18](columnNames(17)) + def c19 = column[A19](columnNames(18)) + def c20 = column[A20](columnNames(19)) + def c21 = column[A21](columnNames(20)) + def c22 = column[A22](columnNames(21)) + + override def id = c1 + + override protected val columns: Seq[(String, this.type => Column[_])] = Seq( + columnNames(0) -> (_.c1), + columnNames(1) -> (_.c2), + columnNames(2) -> (_.c3), + columnNames(3) -> (_.c4), + columnNames(4) -> (_.c5), + columnNames(5) -> (_.c6), + columnNames(6) -> (_.c7), + columnNames(7) -> (_.c8), + columnNames(8) -> (_.c9), + columnNames(9) -> (_.c10), + columnNames(10) -> (_.c11), + columnNames(11) -> (_.c12), + columnNames(12) -> (_.c13), + columnNames(13) -> (_.c14), + columnNames(14) -> (_.c15), + columnNames(15) -> (_.c16), + columnNames(16) -> (_.c17), + columnNames(17) -> (_.c18), + columnNames(18) -> (_.c19), + columnNames(19) -> (_.c20), + columnNames(20) -> (_.c21), + columnNames(21) -> (_.c22) + ) + + def * = (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22) <> (apply.tupled, unapply) + } + } From c75796f28a27427be713ef1e45633cf93785d648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jerzy=20M=C3=BCller?= Date: Wed, 20 Jan 2016 15:18:13 +0100 Subject: [PATCH 3/8] Update build.sbt Bump coverageMInimum to 65% --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index f7186ef..5454c30 100644 --- a/build.sbt +++ b/build.sbt @@ -69,7 +69,7 @@ pomExtra := https://github.com/VirtusLab/beholder // Scoverage setup -ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 48 +ScoverageSbtPlugin.ScoverageKeys.coverageMinimum := 65 ScoverageSbtPlugin.ScoverageKeys.coverageFailOnMinimum := true From 9373238438f907326e8601f88c1498ca290bf192 Mon Sep 17 00:00:00 2001 From: Krzysztof Borowski Date: Tue, 19 Jan 2016 14:29:58 +0100 Subject: [PATCH 4/8] Add disjunction filtering based on enum and int values #17 --- build.sbt | 4 +- .../filters/forms/FormFilterField.scala | 36 +++++++++++-- .../filters/json/FilterController.scala | 9 ++-- .../filters/json/JsonFilterField.scala | 53 ++++++++++++++++++- .../beholder/utils/SeqParametersHelper.scala | 14 +++++ .../org/virtuslab/beholder/BaseTest.scala | 4 +- .../virtuslab/beholder/FormFiltersTests.scala | 46 ++++++++++++++-- .../virtuslab/beholder/JsonFiltersTests.scala | 40 ++++++++++++-- .../virtuslab/beholder/UserMachinesView.scala | 11 ++-- .../beholder/json/JsonFormatterTest.scala | 10 ++-- .../virtuslab/beholder/model/Machine.scala | 48 ++++++++++++++++- .../beholder/suites/EnumFilterTestSuite.scala | 22 ++++++++ .../suites/InitialQueryTestSuite.scala | 9 ++-- .../beholder/suites/SeqFilterTestSuite.scala | 46 ++++++++++++++++ 14 files changed, 318 insertions(+), 34 deletions(-) create mode 100644 src/main/scala/org/virtuslab/beholder/utils/SeqParametersHelper.scala create mode 100644 src/test/scala/org/virtuslab/beholder/suites/EnumFilterTestSuite.scala create mode 100644 src/test/scala/org/virtuslab/beholder/suites/SeqFilterTestSuite.scala diff --git a/build.sbt b/build.sbt index f7186ef..e26a150 100644 --- a/build.sbt +++ b/build.sbt @@ -77,6 +77,6 @@ ScoverageSbtPlugin.ScoverageKeys.coverageExcludedPackages := Seq( "org.virtuslab.beholder.utils.generators.*", // only BaseView5 is tested, all are generated, so there is no need to check them all "org.virtuslab.beholder.views.FilterableViews.*", - "org.virtuslab.beholder.views.FilterableViewsGenerateCode.BaseView[^5].*", + "org.virtuslab.beholder.views.FilterableViewsGenerateCode.BaseView[^6].*", "org.virtuslab.beholder.filters.FilterFactoryMethods.*" -).mkString(";") +).mkString(";") \ No newline at end of file diff --git a/src/main/scala/org/virtuslab/beholder/filters/forms/FormFilterField.scala b/src/main/scala/org/virtuslab/beholder/filters/forms/FormFilterField.scala index df82131..1a663eb 100644 --- a/src/main/scala/org/virtuslab/beholder/filters/forms/FormFilterField.scala +++ b/src/main/scala/org/virtuslab/beholder/filters/forms/FormFilterField.scala @@ -4,7 +4,7 @@ import org.virtuslab.beholder.filters.{ FilterRange, MappedFilterField } import org.virtuslab.beholder.utils.ILikeExtension._ import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ import play.api.data.Forms._ -import play.api.data.format.Formatter +import play.api.data.format.{ Formats, Formatter } import play.api.data.validation.Constraint import play.api.data.{ FormError, Mapping } @@ -19,6 +19,8 @@ abstract class FormFilterField[A: TypedType, B](mapping: Mapping[B]) extends Map object FromFilterFields { + import org.virtuslab.beholder.utils.SeqParametersHelper._ + private def ignoreMapping[T] = new Mapping[T] { val key = "" val mappings: Seq[Mapping[_]] = Nil @@ -49,6 +51,15 @@ object FromFilterFields { override def filterOnColumn(column: Column[Int])(data: Int): Column[Option[Boolean]] = column === data } + /** + * check if value is in given sequence + */ + object inIntFieldSeq extends FormFilterField[Int, Seq[Int]](seq(number)) { + override protected def filterOnColumn(column: Column[Int])(dataSeq: Seq[Int]): Column[Option[Boolean]] = { + isColumnValueInsideSeq(column)(dataSeq)((column, data) => column === data) + } + } + /** * simple check boolean */ @@ -64,8 +75,14 @@ object FromFilterFields { } /** - * search in text (ilike) + * check if text is in given text sequence (ilike) */ + object inTextSeq extends FormFilterField[String, Seq[String]](seq(text)) { + override def filterOnColumn(column: Column[String])(dataSeq: Seq[String]): Column[Option[Boolean]] = { + isColumnValueInsideSeq(column)(dataSeq)((column, d) => column ilike s"%${escape(d)}%") + } + } + object inBigDecimal extends FormFilterField[BigDecimal, BigDecimal](bigDecimal) { override def filterOnColumn(column: Column[BigDecimal])(data: BigDecimal): Column[Option[Boolean]] = column === data } @@ -82,13 +99,26 @@ object FromFilterFields { * @tparam T - enum class (eg. Colors.type) */ def inEnum[T <: Enumeration](implicit tm: BaseTypedType[T#Value], formatter: Formatter[T#Value]): FormFilterField[T#Value, T#Value] = - inField[T#Value] + new FormFilterField[T#Value, T#Value](of[T#Value]) { + override def filterOnColumn(column: Column[T#Value])(data: T#Value): Column[Option[Boolean]] = column === data + } + + def inEnumSeq[T <: Enumeration](implicit tm: BaseTypedType[T#Value], formatter: Formatter[T#Value]): FormFilterField[T#Value, Seq[T#Value]] = { + inFieldSeq[T#Value] + } def inField[T](implicit tm: BaseTypedType[T], formatter: Formatter[T]): FormFilterField[T, T] = new FormFilterField[T, T](of[T]) { override def filterOnColumn(column: Column[T])(data: T): Column[Option[Boolean]] = column === data } + def inFieldSeq[T](implicit tm: BaseTypedType[T], formatter: Formatter[T]): FormFilterField[T, Seq[T]] = + new FormFilterField[T, Seq[T]](seq(of[T])) { + override def filterOnColumn(column: Column[T])(dataSeq: Seq[T]): Column[Option[Boolean]] = { + isColumnValueInsideSeq(column)(dataSeq)((column, data) => column === data) + } + } + def inRange[T](implicit tm: BaseTypedType[T], f: Formatter[T]): FormFilterField[T, FilterRange[T]] = new FormFilterField[T, FilterRange[T]](rangeMapping[T]) { override def filterOnColumn(column: Column[T])(value: FilterRange[T]): Column[Option[Boolean]] = { diff --git a/src/main/scala/org/virtuslab/beholder/filters/json/FilterController.scala b/src/main/scala/org/virtuslab/beholder/filters/json/FilterController.scala index b3937c8..cb2b5e6 100644 --- a/src/main/scala/org/virtuslab/beholder/filters/json/FilterController.scala +++ b/src/main/scala/org/virtuslab/beholder/filters/json/FilterController.scala @@ -21,14 +21,17 @@ trait FilterControllerBase[Context, Entity <: Product] extends Controller { request.body.asJson.map(formatter.filterDefinition).map { fd => fd.map { filterDefinition => - val filterResult = callFilter(context, mapFilterData(filterDefinition)) - formatter.results(filterDefinition, filterResult) + val filterResult = callFilter(context, mapFilterData(filterDefinition, context)) + formatter.results(filterDefinition, modifyFilterResults(filterResult, filterDefinition, context)) } }.getOrElse(JsError("json expected")) } //for filter modification such us setting default parameters etc. - protected def mapFilterData(data: FilterDefinition) = data + protected def mapFilterData(data: FilterDefinition, context: Context) = data + + //for result modification such as sorting or fetching additional data + protected def modifyFilterResults(results: FilterResult[Entity], filterDefinition: FilterDefinition, context: Context) = results } abstract class FilterController[Entity <: Product](filter: FilterAPI[Entity, JsonFormatter[Entity]]) diff --git a/src/main/scala/org/virtuslab/beholder/filters/json/JsonFilterField.scala b/src/main/scala/org/virtuslab/beholder/filters/json/JsonFilterField.scala index 0fd63a0..3564fb7 100644 --- a/src/main/scala/org/virtuslab/beholder/filters/json/JsonFilterField.scala +++ b/src/main/scala/org/virtuslab/beholder/filters/json/JsonFilterField.scala @@ -37,13 +37,24 @@ abstract class ImplicitlyJsonFilterFiled[A: TypedType: Writes, B: Format](dataTy object JsonFilterFields { + import org.virtuslab.beholder.utils.SeqParametersHelper._ + /** - * search in text (ilike) + * find exact number */ object inIntField extends ImplicitlyJsonFilterFiled[Int, Int]("Int") { override protected def filterOnColumn(column: Column[Int])(data: Int): Column[Option[Boolean]] = column === data } + /** + * check if value is in given sequence + */ + object inIntFieldSeq extends ImplicitlyJsonFilterFiled[Int, Seq[Int]]("IntSeq") { + override protected def filterOnColumn(column: Column[Int])(dataSeq: Seq[Int]): Column[Option[Boolean]] = { + isColumnValueInsideSeq(column)(dataSeq)((column, data) => column === data) + } + } + object inBigDecimal extends ImplicitlyJsonFilterFiled[BigDecimal, BigDecimal]("bigDecimal") { override protected def filterOnColumn(column: Column[BigDecimal])(data: BigDecimal): Column[Option[Boolean]] = column === data } @@ -62,6 +73,15 @@ object JsonFilterFields { override def filterOnColumn(column: Column[String])(data: String): Column[Option[Boolean]] = column ilike s"%${escape(data)}%" } + /** + * check if text is in given text sequence (ilike) + */ + object inTextSeq extends ImplicitlyJsonFilterFiled[String, Seq[String]]("TextSeq") { + override def filterOnColumn(column: Column[String])(data: Seq[String]): Column[Option[Boolean]] = { + isColumnValueInsideSeq(column)(data)((column, d) => column ilike s"%${escape(d)}%") + } + } + /** * search in text (ilike) for optional fields */ @@ -103,6 +123,30 @@ object JsonFilterFields { } } + /** + * check if enum value is in given sequence + * @tparam T - enum class (eg. Colors.type) + */ + def inEnumSeq[T <: Enumeration](enum: T)(implicit tm: BaseTypedType[T#Value], formatter: Format[T#Value]): JsonFilterField[T#Value, Seq[T#Value]] = { + new JsonFilterField[T#Value, Seq[T#Value]] { + override def fieldTypeDefinition: JsValue = JsArray( + enum.values.toList.map(v => Json.toJson(v.asInstanceOf[T#Value])) + ) + + override protected[json] def valueWrite: Writes[T#Value] = formatter + + override protected def filterFormat: Format[Seq[T#Value]] = new Format[Seq[T#Value]] { + override def reads(json: JsValue): JsResult[Seq[T#Value]] = JsSuccess(json.as[Seq[T#Value]]) + + override def writes(o: Seq[T#Value]): JsValue = JsArray(o.map(Json.toJson(_))) + } + + override protected def filterOnColumn(column: Column[T#Value])(dataSeq: Seq[T#Value]): Column[Option[Boolean]] = { + isColumnValueInsideSeq(column)(dataSeq)((column, data) => column === data) + } + } + } + private implicit def rangeFormat[T: Format]: Format[FilterRange[T]] = ((__ \ "from").formatNullable[T] and (__ \ "to").formatNullable[T])(FilterRange.apply, unlift(FilterRange.unapply)) @@ -112,6 +156,13 @@ object JsonFilterFields { override def filterOnColumn(column: Column[T])(data: T): Column[Option[Boolean]] = column === data } + def inFieldSeq[T: BaseTypedType: Format](typeName: String) = + new ImplicitlyJsonFilterFiled[T, Seq[T]](typeName) { + override def filterOnColumn(column: Column[T])(dataSeq: Seq[T]): Column[Option[Boolean]] = { + isColumnValueInsideSeq(column)(dataSeq)((column, data) => column === data) + } + } + def inRange[T: BaseTypedType: Format](baseType: JsonFilterField[T, T]): JsonFilterField[T, FilterRange[T]] = new JsonFilterField[T, FilterRange[T]] { override def filterOnColumn(column: Column[T])(value: FilterRange[T]): Column[Option[Boolean]] = { diff --git a/src/main/scala/org/virtuslab/beholder/utils/SeqParametersHelper.scala b/src/main/scala/org/virtuslab/beholder/utils/SeqParametersHelper.scala new file mode 100644 index 0000000..d8f24d3 --- /dev/null +++ b/src/main/scala/org/virtuslab/beholder/utils/SeqParametersHelper.scala @@ -0,0 +1,14 @@ +package org.virtuslab.beholder.utils + +import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ + +object SeqParametersHelper { + + def isColumnValueInsideSeq[A](column: Column[A])(dataSeq: Seq[A])(isEqual: (Column[A], A) => Column[Option[Boolean]]): Column[Option[Boolean]] = { + if (dataSeq.isEmpty) { + LiteralColumn(Option(true)) + } else { + dataSeq.foldLeft(Option(false): Column[Option[Boolean]])((acc, data) => acc || isEqual(column, data)) + } + } +} diff --git a/src/test/scala/org/virtuslab/beholder/BaseTest.scala b/src/test/scala/org/virtuslab/beholder/BaseTest.scala index bcaf0f4..ebc2fb2 100644 --- a/src/test/scala/org/virtuslab/beholder/BaseTest.scala +++ b/src/test/scala/org/virtuslab/beholder/BaseTest.scala @@ -40,8 +40,8 @@ trait ModelIncluded { ).map(user => user.copy(id = Some(UsersRepository.save(user)))) val machines = Seq( - Machine(None, "a.a.pl", "Ubuntu", 4, new Date(DateTime.now().minusHours(24).getMillis), Some(1)), - Machine(None, "o.a.pl", "Fedora", 1, new Date(DateTime.now().getMillis), Some(3)) + Machine(None, "a.a.pl", "Ubuntu", 4, new Date(DateTime.now().minusHours(24).getMillis), Some(1), MachineStatus.Inactive), + Machine(None, "o.a.pl", "Fedora", 1, new Date(DateTime.now().getMillis), Some(3), MachineStatus.Active) ).map(machine => machine.copy(id = Some(MachineRepository.save(machine)))) val Seq(user1, user2) = users diff --git a/src/test/scala/org/virtuslab/beholder/FormFiltersTests.scala b/src/test/scala/org/virtuslab/beholder/FormFiltersTests.scala index 267e3d0..5cdbc87 100644 --- a/src/test/scala/org/virtuslab/beholder/FormFiltersTests.scala +++ b/src/test/scala/org/virtuslab/beholder/FormFiltersTests.scala @@ -5,7 +5,8 @@ import java.sql.Date import org.virtuslab.beholder.filters.forms.FromFilterFields._ import org.virtuslab.beholder.filters.forms.{ FormFilters, FormFormatter, FromFilterFields } import org.virtuslab.beholder.filters.{ FilterAPI, FilterDefinition } -import org.virtuslab.beholder.suites.{ InitialQueryTestSuite, BaseSuite, FiltersTestSuite, RangeFiltersSuite } +import org.virtuslab.beholder.model.MachineStatus +import org.virtuslab.beholder.suites._ import org.virtuslab.unicorn.LongUnicornPlay._ import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ @@ -15,6 +16,7 @@ trait FormFiltersTestsBase { override def doFilters(data: BaseFilterData, currentFilter: FilterDefinition): Seq[UserMachineViewRow] = { val formatter = data.filter.formatter val formData = formatter.filterForm.fill(currentFilter) + formatter.filterForm.bind(formData.data).fold( errors => fail(s"Form errors ${errors.errors.mkString}"), fromForm => data.filter.filter(fromForm)(data.session) @@ -33,7 +35,8 @@ class FormFiltersTests extends AppTest with FiltersTestSuite[FormFormatter] with inText, inIntField, inRange[Date], - FromFilterFields.ignore[Option[BigDecimal]] + FromFilterFields.ignore[Option[BigDecimal]], + FromFilterFields.ignore[MachineStatus.Value] ) }.filterGenerator } @@ -51,7 +54,44 @@ class FormRangeFiltersTests extends AppTest with RangeFiltersSuite[FormFormatter inText, inRange[Int], inRange[Date], - inOptionRange[BigDecimal] + inOptionRange[BigDecimal], + FromFilterFields.ignore[MachineStatus.Value] + ) + }.filterGenerator + +} + +class FormEnumFiltersTests extends AppTest with EnumFilterTestSuite[FormFormatter] with FormFiltersTestsBase { + def createFilter(data: BaseFilterData): FilterAPI[UserMachineViewRow, FormFormatter] = new CustomTypeMappers { + + import play.api.data.format.Formats._ + + val filterGenerator = new FormFilters[UserMachineViewRow].create( + data.view, + inText, + inText, + inIntField, + inRange[Date], + inOptionRange[BigDecimal], + inEnum[MachineStatus.type] + ) + }.filterGenerator + +} + +class FormSeqFiltersTests extends AppTest with SeqFilterTestSuite[FormFormatter] with FormFiltersTestsBase { + def createFilter(data: BaseFilterData): FilterAPI[UserMachineViewRow, FormFormatter] = new CustomTypeMappers { + + import play.api.data.format.Formats._ + + val filterGenerator = new FormFilters[UserMachineViewRow].create( + data.view, + inText, + inText, + inIntFieldSeq, + inRange[Date], + inOptionRange[BigDecimal], + inEnumSeq[MachineStatus.type] ) }.filterGenerator diff --git a/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala b/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala index c42d02e..41cf753 100644 --- a/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala +++ b/src/test/scala/org/virtuslab/beholder/JsonFiltersTests.scala @@ -2,10 +2,14 @@ package org.virtuslab.beholder import java.sql.Date -import org.virtuslab.beholder.filters.json.JsonFilterFields.{ inIntField, inOptionRange, inRange, _ } +import org.virtuslab.beholder.filters.json.JsonFilterFields.inField +import org.virtuslab.beholder.filters.json.JsonFilterFields.inIntFieldSeq +import org.virtuslab.beholder.filters.json.JsonFilterFields.inText +import org.virtuslab.beholder.filters.json.JsonFilterFields.{ inIntField, inOptionRange, inRange, inEnum, inEnumSeq } import org.virtuslab.beholder.filters.json.{ JsonFilterFields, JsonFilters, JsonFormatter } import org.virtuslab.beholder.filters.{ FilterAPI, FilterDefinition } -import org.virtuslab.beholder.suites.{ InitialQueryTestSuite, BaseSuite, FiltersTestSuite, RangeFiltersSuite } +import org.virtuslab.beholder.model.MachineStatus +import org.virtuslab.beholder.suites._ import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ import play.api.libs.json.{ JsSuccess, JsObject } @@ -33,7 +37,8 @@ class JsonFiltersTests extends AppTest with FiltersTestSuite[JsonFormatter[UserM inText, inIntField, inRange(inField[Date]("date")), - JsonFilterFields.ignore[Option[BigDecimal]] + JsonFilterFields.ignore[Option[BigDecimal]], + inEnum(MachineStatus) ) } @@ -47,6 +52,33 @@ class JsonFiltersRangeTests extends AppTest with RangeFiltersSuite[JsonFormatter inText, inRange(inIntField), inRange(inField[Date]("date")), - inOptionRange(inField[BigDecimal]("number")) + inOptionRange(inField[BigDecimal]("number")), + inEnum(MachineStatus) + ) +} + +class JsonFiltersEnumTests extends AppTest with EnumFilterTestSuite[JsonFormatter[UserMachineViewRow]] with JsonFiltersTestsBase { + def createFilter(data: BaseFilterData): FilterAPI[UserMachineViewRow, JsonFormatter[UserMachineViewRow]] = + new JsonFilters[UserMachineViewRow](identity).create( + data.view, + inText, + inText, + inIntFieldSeq, + inRange(inField[Date]("date")), + inOptionRange(inField[BigDecimal]("number")), + inEnum(MachineStatus) + ) +} + +class JsonFiltersSeqTests extends AppTest with SeqFilterTestSuite[JsonFormatter[UserMachineViewRow]] with JsonFiltersTestsBase { + def createFilter(data: BaseFilterData): FilterAPI[UserMachineViewRow, JsonFormatter[UserMachineViewRow]] = + new JsonFilters[UserMachineViewRow](identity).create( + data.view, + inText, + inText, + inIntFieldSeq, + inRange(inField[Date]("date")), + inOptionRange(inField[BigDecimal]("number")), + inEnumSeq(MachineStatus) ) } \ No newline at end of file diff --git a/src/test/scala/org/virtuslab/beholder/UserMachinesView.scala b/src/test/scala/org/virtuslab/beholder/UserMachinesView.scala index 8a00a1f..98fba57 100644 --- a/src/test/scala/org/virtuslab/beholder/UserMachinesView.scala +++ b/src/test/scala/org/virtuslab/beholder/UserMachinesView.scala @@ -2,17 +2,19 @@ package org.virtuslab.beholder import java.sql.Date -import org.virtuslab.beholder.model.{ Machines, Users } +import org.virtuslab.beholder.model.{ MachineStatus, Machines, Users } import org.virtuslab.beholder.views.FilterableViews import org.virtuslab.unicorn.LongUnicornPlay._ import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ +import play.api.libs.json._ case class UserMachineViewRow( email: String, system: String, cores: Int, created: Date, - capacity: Option[BigDecimal] + capacity: Option[BigDecimal], + status: MachineStatus.Value ) trait UserMachinesView extends ModelIncluded { @@ -28,7 +30,7 @@ trait UserMachinesView extends ModelIncluded { } yield (user, machine) val tableQuery = FilterableViews.createView( - name = "USERS_MACHINE_VIEW", + name = "USER_MACHINE_VIEW", UserMachineViewRow.apply _, UserMachineViewRow.unapply _, baseQuery = usersMachinesQuery @@ -39,7 +41,8 @@ trait UserMachinesView extends ModelIncluded { "system" -> machine.system, "cores" -> machine.cores, "created" -> machine.created, - "capacity" -> machine.capacity) + "capacity" -> machine.capacity, + "status" -> machine.status) } tableQuery.viewDDL.create diff --git a/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala b/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala index 48c8428..f2470ae 100644 --- a/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala +++ b/src/test/scala/org/virtuslab/beholder/json/JsonFormatterTest.scala @@ -5,6 +5,7 @@ import java.sql.Date import org.virtuslab.beholder.filters.FilterDefinition import org.virtuslab.beholder.filters.json.JsonFilterFields._ import org.virtuslab.beholder.filters.json.{ JsonFilterFields, JsonFilters } +import org.virtuslab.beholder.model.MachineStatus import org.virtuslab.beholder.{ UserMachineViewRow, _ } import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ import play.api.libs.json.{ JsSuccess, JsArray, JsObject, JsString } @@ -18,10 +19,11 @@ class JsonFormatterTest extends AppTest with UserMachinesView with ModelIncluded new JsonFilters[UserMachineViewRow](labels).create( view, inText, - inText, - inIntField, + inTextSeq, + inIntFieldSeq, inRange(inField[Date]("date")), - JsonFilterFields.ignore[Option[BigDecimal]] + JsonFilterFields.ignore[Option[BigDecimal]], + JsonFilterFields.ignore[MachineStatus.Value] ) } @@ -32,7 +34,7 @@ class JsonFormatterTest extends AppTest with UserMachinesView with ModelIncluded val req = JsObject(Seq("data" -> JsObject(Seq("email" -> JsString("ala"))))) - val data = FilterDefinition(None, None, None, Seq(Some("ala"), None, None, None, None)) + val data = FilterDefinition(None, None, None, Seq(Some("ala"), None, None, None, None, None)) filter.formatter.filterDefinition(req) shouldEqual JsSuccess(data) } diff --git a/src/test/scala/org/virtuslab/beholder/model/Machine.scala b/src/test/scala/org/virtuslab/beholder/model/Machine.scala index feafd80..5e5a6e8 100644 --- a/src/test/scala/org/virtuslab/beholder/model/Machine.scala +++ b/src/test/scala/org/virtuslab/beholder/model/Machine.scala @@ -4,7 +4,48 @@ import java.sql.Date import org.virtuslab.unicorn.LongUnicornPlay._ import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ +import play.api.data.FormError +import play.api.libs.json._ +import play.api.data.format.{ Formats, Formatter } +/** + * commons enum method for use with slick and play + */ +trait BaseEnum { + self: Enumeration => + + /** + * Type mapper placed here is resolved automatically and does not need to be imported anywhere. + */ + implicit val typeMapper: BaseColumnType[Value] = MappedColumnType.base[Value, Int](int => int.id, id => apply(id)) + + //for play forms + implicit lazy val mappingFormatter: Formatter[Value] = new Formatter[Value] { + + override val format = Some(("format.numeric", Nil)) + + override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], Value] = + Formats.intFormat.bind(key, data).right.map(apply) + + override def unbind(key: String, value: Value): Map[String, String] = + Map(key -> value.id.toString) + } + + //for json forms + implicit val format = new Format[Value] { + override def writes(o: Value): JsValue = JsNumber(o.id) + override def reads(json: JsValue): JsResult[Value] = json.asOpt[Int].map(apply).map(JsSuccess(_)).getOrElse(JsError("format invalid")) + } + +} + +object MachineStatus extends Enumeration with BaseEnum { + + val Active = Value(1, "active") + val Inactive = Value(2, "inactive") + val Broken = Value(3, "broken") + +} /** Id class for type-safe joins and queries. */ case class MachineId(id: Long) extends AnyVal with BaseId @@ -29,7 +70,8 @@ case class Machine( system: String, cores: Int, created: Date, - capacity: Option[BigDecimal] + capacity: Option[BigDecimal], + status: MachineStatus.Value ) extends WithId[MachineId] /** Table definition for machines. */ @@ -45,5 +87,7 @@ class Machines(tag: Tag) extends IdTable[MachineId, Machine](tag, "MACHINES") { def capacity = column[Option[BigDecimal]]("capacity") - override def * = (id.?, url, system, cores, created, capacity) <> (Machine.tupled, Machine.unapply) + def status = column[MachineStatus.Value]("status") + + override def * = (id.?, url, system, cores, created, capacity, status) <> (Machine.tupled, Machine.unapply) } diff --git a/src/test/scala/org/virtuslab/beholder/suites/EnumFilterTestSuite.scala b/src/test/scala/org/virtuslab/beholder/suites/EnumFilterTestSuite.scala new file mode 100644 index 0000000..5721fe6 --- /dev/null +++ b/src/test/scala/org/virtuslab/beholder/suites/EnumFilterTestSuite.scala @@ -0,0 +1,22 @@ +package org.virtuslab.beholder.suites + +import org.virtuslab.beholder.AppTest +import org.virtuslab.beholder.model.MachineStatus + +trait EnumFilterTestSuite[Formatter] extends BaseSuite[Formatter] { + self: AppTest => + + it should "filter all users with inactive machines" in baseFilterTest { + data => + import data._ + + val a = baseFilter.data + val inactive = Some(MachineStatus.Inactive) + + val usersWithInactiveMachines = doFilters(data, baseFilter.copy(data = a.updated(5, inactive))) + + //usersWithInactiveMachines.size should be(2) + usersWithInactiveMachines should contain theSameElementsAs allFromDb.filter(machine => machine.status == MachineStatus.Inactive) + } + +} diff --git a/src/test/scala/org/virtuslab/beholder/suites/InitialQueryTestSuite.scala b/src/test/scala/org/virtuslab/beholder/suites/InitialQueryTestSuite.scala index 55fa582..5b4cce2 100644 --- a/src/test/scala/org/virtuslab/beholder/suites/InitialQueryTestSuite.scala +++ b/src/test/scala/org/virtuslab/beholder/suites/InitialQueryTestSuite.scala @@ -3,7 +3,7 @@ package org.virtuslab.beholder.suites import java.sql.Date import org.joda.time.DateTime -import org.virtuslab.beholder.model.{ Machine, User } +import org.virtuslab.beholder.model.{ MachineStatus, Machine, User } import org.virtuslab.beholder.views.FilterableViews import org.virtuslab.beholder.{ UserMachineViewRow, AppTest } import org.virtuslab.beholder.filters.{ TableFilterAPI, FilterAPI } @@ -11,9 +11,6 @@ import org.virtuslab.unicorn.LongUnicornPlay.driver.simple._ import scala.tools.nsc.doc.model.Entity -/** - * Author: Krzysztof Romanowski - */ trait InitialQueryTestSuite[Formatter] extends FiltersTestSuite[Formatter] { self: AppTest => override protected def baseFilterTest[A](testImplementation: (BaseFilterData) => A): A = @@ -28,7 +25,7 @@ trait InitialQueryTestSuite[Formatter] extends FiltersTestSuite[Formatter] { class InitialQueryFilterData(implicit session: Session) extends BaseFilterData { override lazy val filter: FilterAPI[UserMachineViewRow, Formatter] = createFilter(this) - .asInstanceOf[TableFilterAPI[UserMachineViewRow, Formatter, FilterableViews.BaseView5[_, String, _, _, _, _]]] + .asInstanceOf[TableFilterAPI[UserMachineViewRow, Formatter, FilterableViews.BaseView6[_, String, _, _, _, _, _]]] .withInitialFilter(table => !(table.c1 === newMail)) //cos !== is not working val newUser = { @@ -37,7 +34,7 @@ trait InitialQueryTestSuite[Formatter] extends FiltersTestSuite[Formatter] { } val newMachine = { - val m = Machine(None, "b.pl", "Windows", 6, new Date(DateTime.now().getMillis), Some(12)) + val m = Machine(None, "b.pl", "Windows", 6, new Date(DateTime.now().getMillis), Some(12), MachineStatus.Inactive) m.copy(id = Some(MachineRepository.save(m))) } diff --git a/src/test/scala/org/virtuslab/beholder/suites/SeqFilterTestSuite.scala b/src/test/scala/org/virtuslab/beholder/suites/SeqFilterTestSuite.scala new file mode 100644 index 0000000..356b072 --- /dev/null +++ b/src/test/scala/org/virtuslab/beholder/suites/SeqFilterTestSuite.scala @@ -0,0 +1,46 @@ +package org.virtuslab.beholder.suites + +import org.virtuslab.beholder.AppTest +import org.virtuslab.beholder.model.MachineStatus + +trait SeqFilterTestSuite[Formatter] extends BaseSuite[Formatter] { + self: AppTest => + + it should "filter by seq(int) only users with one and four core machines" in baseFilterTest { + data => + import data._ + + val a = baseFilter.data + val usersWithOneOrFourCore = Some(Seq(1, 4)) + + val usersWithOneOrFourCoreMachines = doFilters(data, baseFilter.copy(data = a.updated(2, usersWithOneOrFourCore))) + + usersWithOneOrFourCoreMachines should contain theSameElementsAs allFromDb + } + + it should "filter by seq(int) only users with one and three core machine" in baseFilterTest { + data => + import data._ + + val a = baseFilter.data + val oneOrThreeCore = Some(Seq(1, 3)) + + val usersWithOneOrThreeCoreMachines = doFilters(data, baseFilter.copy(data = a.updated(2, oneOrThreeCore))) + + usersWithOneOrThreeCoreMachines should contain theSameElementsAs allFromDb.filter(machine => machine.cores == 1 || machine.cores == 3) + } + + it should "filter by seq(enum) all users together with inactive and broken machines" in baseFilterTest { + data => + import data._ + + val a = baseFilter.data + val inactiveAndBroken = Some(Seq(MachineStatus.Inactive, MachineStatus.Broken)) + + val usersWithInactiveAndBrokenMachines = doFilters(data, baseFilter.copy(data = a.updated(5, inactiveAndBroken))) + + usersWithInactiveAndBrokenMachines.size should be(2) + usersWithInactiveAndBrokenMachines should contain theSameElementsAs allFromDb.filter(machine => machine.status == MachineStatus.Inactive) + } + +} From e1111186914e9f76b89c8140919cebfd121f450b Mon Sep 17 00:00:00 2001 From: liosedhel Date: Mon, 7 Mar 2016 15:24:58 +0100 Subject: [PATCH 5/8] Setting version to 0.2.10 --- build.sbt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 9de4e6e..35a85a6 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ organization := "org.virtuslab" name := "beholder" -version := "0.2.10-SNAPSHOT" +version := "0.2.10" scalaVersion := "2.11.7" @@ -65,7 +65,6 @@ pomExtra := https://github.com/VirtusLab/beholder -//xerial.sbt.Sonatype.sonatypeSettings // Scoverage setup @@ -79,4 +78,7 @@ ScoverageSbtPlugin.ScoverageKeys.coverageExcludedPackages := Seq( "org.virtuslab.beholder.views.FilterableViews.*", "org.virtuslab.beholder.views.FilterableViewsGenerateCode.BaseView[^6].*", "org.virtuslab.beholder.filters.FilterFactoryMethods.*" -).mkString(";") \ No newline at end of file +).mkString(";") + +xerial.sbt.Sonatype.sonatypeSettings + From 432944e8eccf1efc2592924fcb497c6de069af34 Mon Sep 17 00:00:00 2001 From: liosedhel Date: Mon, 7 Mar 2016 15:49:05 +0100 Subject: [PATCH 6/8] Bump to 0.2.11-SNAPSHOT --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 35a85a6..9d0b615 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ organization := "org.virtuslab" name := "beholder" -version := "0.2.10" +version := "0.2.11-SNAPSHOT" scalaVersion := "2.11.7" From 1fdd0f9269b997e716e023f6a3972744ed16bcdc Mon Sep 17 00:00:00 2001 From: Mikhail Fedortsov Date: Tue, 7 Mar 2017 16:14:20 +0100 Subject: [PATCH 7/8] remove ordering from count query --- .../beholder/filters/BaseFilter.scala | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala b/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala index 75b868c..77b79ab 100644 --- a/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala +++ b/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala @@ -79,14 +79,16 @@ abstract class BaseFilter[Id, Entity, FilterTable <: BaseView[Id, Entity], Field private def createFilter(data: FilterDefinition, initialFilter: FilterTable => Column[Boolean]): FilterQuery = { table.filter(filters(data.data, initialFilter)) - .sortBy { - inQueryTable => - val globalColumns = - order(data)(inQueryTable).map { - case (column, asc) => if (asc) column.asc else column.desc - }.toSeq.flatMap(_.columns) - new Ordered(globalColumns ++ inQueryTable.id.asc.columns) - } + } + + private def addOrdering(data: FilterDefinition, query: FilterQuery): FilterQuery = { + query.sortBy { + inQueryTable => + val globalColumns = order(data)(inQueryTable).map { + case (column, asc) => if (asc) column.asc else column.desc + }.toSeq.flatMap(_.columns) + new Ordered(globalColumns ++ inQueryTable.id.asc.columns) + } } private def takeAndSkip(data: FilterDefinition, filter: FilterQuery)(implicit session: Session): Seq[Entity] = { @@ -107,7 +109,8 @@ abstract class BaseFilter[Id, Entity, FilterTable <: BaseView[Id, Entity], Field initialFilter: FilterTable => Column[Boolean] )(implicit session: Session): FilterResult[Entity] = { val filter = createFilter(data, initialFilter) - FilterResult(takeAndSkip(data, filter), filter.length.run) + val filterWithOrdering = addOrdering(data, filter) + FilterResult(takeAndSkip(data, filterWithOrdering), filter.length.run) } //ordering From 4cf8d1395eabbc2cc3ee8870c0fada850d061873 Mon Sep 17 00:00:00 2001 From: mfedortsov Date: Tue, 7 Mar 2017 17:46:26 +0100 Subject: [PATCH 8/8] fix failed tests --- src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala b/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala index 77b79ab..9df3775 100644 --- a/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala +++ b/src/main/scala/org/virtuslab/beholder/filters/BaseFilter.scala @@ -102,7 +102,7 @@ abstract class BaseFilter[Id, Entity, FilterTable <: BaseView[Id, Entity], Field data: FilterDefinition, initialFilter: FilterTable => Column[Boolean] )(implicit session: Session): Seq[Entity] = - takeAndSkip(data, createFilter(data, initialFilter)) + takeAndSkip(data, addOrdering(data, createFilter(data, initialFilter))) override protected def doFilterWithTotalEntitiesNumber( data: FilterDefinition,