mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-08-22 02:02:14 +00:00
Support record owner group filtering in record set search (#936)
* Support record owner group filter in list record set calls. * Update unit tests. * Update portal UI.
This commit is contained in:
parent
1e6dad534d
commit
5d2f2b87da
@ -180,6 +180,7 @@ class RecordSetService(
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: String,
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort,
|
||||
authPrincipal: AuthPrincipal
|
||||
): Result[ListGlobalRecordSetsResponse] =
|
||||
@ -193,6 +194,7 @@ class RecordSetService(
|
||||
maxItems,
|
||||
Some(formattedRecordNameFilter),
|
||||
recordTypeFilter,
|
||||
recordOwnerGroupFilter,
|
||||
nameSort
|
||||
)
|
||||
.toResult[ListRecordSetResults]
|
||||
@ -208,6 +210,7 @@ class RecordSetService(
|
||||
recordSetResults.maxItems,
|
||||
recordNameFilter,
|
||||
recordSetResults.recordTypeFilter,
|
||||
recordSetResults.recordOwnerGroupFilter,
|
||||
recordSetResults.nameSort
|
||||
)
|
||||
|
||||
@ -217,6 +220,7 @@ class RecordSetService(
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort,
|
||||
authPrincipal: AuthPrincipal
|
||||
): Result[ListRecordSetsByZoneResponse] =
|
||||
@ -230,6 +234,7 @@ class RecordSetService(
|
||||
maxItems,
|
||||
recordNameFilter,
|
||||
recordTypeFilter,
|
||||
recordOwnerGroupFilter,
|
||||
nameSort
|
||||
)
|
||||
.toResult[ListRecordSetResults]
|
||||
@ -244,6 +249,7 @@ class RecordSetService(
|
||||
recordSetResults.maxItems,
|
||||
recordSetResults.recordNameFilter,
|
||||
recordSetResults.recordTypeFilter,
|
||||
recordSetResults.recordOwnerGroupFilter,
|
||||
recordSetResults.nameSort
|
||||
)
|
||||
|
||||
|
@ -52,6 +52,7 @@ trait RecordSetServiceAlgebra {
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: String,
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupId: Option[String],
|
||||
nameSort: NameSort,
|
||||
authPrincipal: AuthPrincipal
|
||||
): Result[ListGlobalRecordSetsResponse]
|
||||
@ -62,6 +63,7 @@ trait RecordSetServiceAlgebra {
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupId: Option[String],
|
||||
nameSort: NameSort,
|
||||
authPrincipal: AuthPrincipal
|
||||
): Result[ListRecordSetsByZoneResponse]
|
||||
|
@ -108,6 +108,7 @@ case class VinylDNSZoneViewLoader(zone: Zone, recordSetRepository: RecordSetRepo
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
.map { result =>
|
||||
|
@ -38,6 +38,7 @@ case class ListGlobalRecordSetsResponse(
|
||||
maxItems: Option[Int] = None,
|
||||
recordNameFilter: String,
|
||||
recordTypeFilter: Option[Set[RecordType]] = None,
|
||||
recordOwnerGroupFilter: Option[String] = None,
|
||||
nameSort: NameSort
|
||||
)
|
||||
|
||||
@ -48,6 +49,7 @@ case class ListRecordSetsByZoneResponse(
|
||||
maxItems: Option[Int] = None,
|
||||
recordNameFilter: Option[String] = None,
|
||||
recordTypeFilter: Option[Set[RecordType]] = None,
|
||||
recordOwnerGroupFilter: Option[String] = None,
|
||||
nameSort: NameSort
|
||||
)
|
||||
|
||||
@ -91,6 +93,7 @@ class RecordSetRoute(
|
||||
"maxItems".as[Int].?(DEFAULT_MAX_ITEMS),
|
||||
"recordNameFilter".?,
|
||||
"recordTypeFilter".?,
|
||||
"recordOwnerGroupFilter".?,
|
||||
"nameSort".as[String].?("ASC")
|
||||
) {
|
||||
(
|
||||
@ -98,6 +101,7 @@ class RecordSetRoute(
|
||||
maxItems: Int,
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[String],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: String
|
||||
) =>
|
||||
val convertedRecordTypeFilter = convertRecordTypeFilter(recordTypeFilter)
|
||||
@ -114,6 +118,7 @@ class RecordSetRoute(
|
||||
Some(maxItems),
|
||||
recordNameFilter,
|
||||
convertedRecordTypeFilter,
|
||||
recordOwnerGroupFilter,
|
||||
NameSort.find(nameSort),
|
||||
_
|
||||
)
|
||||
@ -132,6 +137,7 @@ class RecordSetRoute(
|
||||
"maxItems".as[Int].?(DEFAULT_MAX_ITEMS),
|
||||
"recordNameFilter".as[String],
|
||||
"recordTypeFilter".?,
|
||||
"recordOwnerGroupFilter".?,
|
||||
"nameSort".as[String].?("ASC")
|
||||
) {
|
||||
(
|
||||
@ -139,6 +145,7 @@ class RecordSetRoute(
|
||||
maxItems: Int,
|
||||
recordNameFilter: String,
|
||||
recordTypeFilter: Option[String],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: String
|
||||
) =>
|
||||
val convertedRecordTypeFilter = convertRecordTypeFilter(recordTypeFilter)
|
||||
@ -154,6 +161,7 @@ class RecordSetRoute(
|
||||
Some(maxItems),
|
||||
recordNameFilter,
|
||||
convertedRecordTypeFilter,
|
||||
recordOwnerGroupFilter,
|
||||
NameSort.find(nameSort),
|
||||
_
|
||||
)
|
||||
|
@ -974,7 +974,8 @@ class RecordSetServiceSpec
|
||||
ListRecordSetResults(
|
||||
List(sharedZoneRecord),
|
||||
recordNameFilter = Some("aaaa*"),
|
||||
nameSort = NameSort.ASC
|
||||
nameSort = NameSort.ASC,
|
||||
recordOwnerGroupFilter = Some("owner group id")
|
||||
)
|
||||
)
|
||||
).when(mockRecordRepo)
|
||||
@ -984,6 +985,7 @@ class RecordSetServiceSpec
|
||||
maxItems = any[Option[Int]],
|
||||
recordNameFilter = any[Option[String]],
|
||||
recordTypeFilter = any[Option[Set[RecordType.RecordType]]],
|
||||
recordOwnerGroupFilter = any[Option[String]],
|
||||
nameSort = any[NameSort.NameSort]
|
||||
)
|
||||
|
||||
@ -994,6 +996,7 @@ class RecordSetServiceSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = "aaaa*",
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = Some("owner group id"),
|
||||
nameSort = NameSort.ASC,
|
||||
authPrincipal = sharedAuth
|
||||
)
|
||||
@ -1010,7 +1013,7 @@ class RecordSetServiceSpec
|
||||
)
|
||||
}
|
||||
|
||||
"fail if recordNameFilter is less than two characters" in {
|
||||
"fail if recordNameFilter is fewer than two characters" in {
|
||||
val result = leftResultOf(
|
||||
underTest
|
||||
.listRecordSets(
|
||||
@ -1018,6 +1021,7 @@ class RecordSetServiceSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = "a",
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = Some("owner group id"),
|
||||
nameSort = NameSort.ASC,
|
||||
authPrincipal = okAuth
|
||||
)
|
||||
@ -1047,6 +1051,7 @@ class RecordSetServiceSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -1059,6 +1064,7 @@ class RecordSetServiceSpec
|
||||
recordNameFilter = None,
|
||||
authPrincipal = sharedAuth,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
.value
|
||||
@ -1088,6 +1094,7 @@ class RecordSetServiceSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -1099,6 +1106,7 @@ class RecordSetServiceSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC,
|
||||
authPrincipal = AuthPrincipal(okAuth.signedInUser.copy(isSupport = true), Seq.empty)
|
||||
)
|
||||
@ -1117,6 +1125,7 @@ class RecordSetServiceSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC,
|
||||
authPrincipal = okAuth
|
||||
)
|
||||
|
@ -88,7 +88,7 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
|
||||
"load the DNS Zones" in {
|
||||
val mockRecordSetRepo = mock[RecordSetRepository]
|
||||
|
||||
doReturn(IO(ListRecordSetResults(records, None, None, None, None, None, NameSort.ASC)))
|
||||
doReturn(IO(ListRecordSetResults(records, None, None, None, None, None, None, NameSort.ASC)))
|
||||
.when(mockRecordSetRepo)
|
||||
.listRecordSets(
|
||||
any[Option[String]],
|
||||
@ -96,6 +96,7 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
|
||||
any[Option[Int]],
|
||||
any[Option[String]],
|
||||
any[Option[Set[RecordType]]],
|
||||
any[Option[String]],
|
||||
any[NameSort]
|
||||
)
|
||||
|
||||
|
@ -170,7 +170,7 @@ class ZoneSyncHandlerSpec
|
||||
reset(mockVinylDNSLoader)
|
||||
|
||||
doReturn(
|
||||
IO(ListRecordSetResults(List(testRecord1), None, None, None, None, None, NameSort.ASC))
|
||||
IO(ListRecordSetResults(List(testRecord1), None, None, None, None, None, None, NameSort.ASC))
|
||||
).when(recordSetRepo)
|
||||
.listRecordSets(
|
||||
any[Option[String]],
|
||||
@ -178,6 +178,7 @@ class ZoneSyncHandlerSpec
|
||||
any[Option[Int]],
|
||||
any[Option[String]],
|
||||
any[Option[Set[RecordType]]],
|
||||
any[Option[String]],
|
||||
any[NameSort]
|
||||
)
|
||||
doReturn(IO(testChangeSet)).when(recordSetRepo).apply(any[ChangeSet])
|
||||
|
@ -52,6 +52,7 @@ trait EmptyRecordSetRepo extends RecordSetRepository {
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort
|
||||
): IO[ListRecordSetResults] =
|
||||
IO.pure(ListRecordSetResults(nameSort = nameSort))
|
||||
|
@ -181,7 +181,8 @@ class RecordSetRoutingSpec
|
||||
RecordSetStatus.Active,
|
||||
DateTime.now,
|
||||
None,
|
||||
List(AData("10.1.1.1"))
|
||||
List(AData("10.1.1.1")),
|
||||
ownerGroupId = Some("my-group")
|
||||
)
|
||||
|
||||
private val rs3 = RecordSet(
|
||||
@ -516,6 +517,7 @@ class RecordSetRoutingSpec
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: String,
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort,
|
||||
authPrincipal: AuthPrincipal
|
||||
): Result[ListGlobalRecordSetsResponse] = {
|
||||
@ -530,22 +532,29 @@ class RecordSetRoutingSpec
|
||||
maxItems,
|
||||
"rs*",
|
||||
recordTypeFilter,
|
||||
recordOwnerGroupFilter,
|
||||
nameSort
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Right(
|
||||
ListGlobalRecordSetsResponse(
|
||||
val recordSetList = recordOwnerGroupFilter match {
|
||||
case Some("my-group") => List(RecordSetGlobalInfo(rs2, okZone.name, okZone.shared, None))
|
||||
case _ =>
|
||||
List(
|
||||
RecordSetGlobalInfo(rs1, okZone.name, okZone.shared, None),
|
||||
RecordSetGlobalInfo(rs2, okZone.name, okZone.shared, None),
|
||||
RecordSetGlobalInfo(rs3, okZone.name, okZone.shared, None)
|
||||
),
|
||||
)
|
||||
}
|
||||
Right(
|
||||
ListGlobalRecordSetsResponse(
|
||||
recordSetList,
|
||||
startFrom,
|
||||
None,
|
||||
maxItems,
|
||||
"rs*",
|
||||
recordTypeFilter,
|
||||
None,
|
||||
nameSort
|
||||
)
|
||||
)
|
||||
@ -558,6 +567,7 @@ class RecordSetRoutingSpec
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort,
|
||||
authPrincipal: AuthPrincipal
|
||||
): Result[ListRecordSetsByZoneResponse] = {
|
||||
@ -574,6 +584,7 @@ class RecordSetRoutingSpec
|
||||
maxItems,
|
||||
recordNameFilter,
|
||||
recordTypeFilter,
|
||||
recordOwnerGroupFilter,
|
||||
nameSort
|
||||
)
|
||||
)
|
||||
@ -590,6 +601,7 @@ class RecordSetRoutingSpec
|
||||
maxItems,
|
||||
recordNameFilter,
|
||||
recordTypeFilter,
|
||||
None,
|
||||
nameSort
|
||||
)
|
||||
)
|
||||
@ -955,6 +967,15 @@ class RecordSetRoutingSpec
|
||||
.only(rs1.id, rs2.id, rs3.id)
|
||||
}
|
||||
}
|
||||
|
||||
"return all recordsets of a specific owner group" in {
|
||||
Get(s"/recordsets?recordNameFilter=rs*&recordOwnerGroupFilter=my-group") ~> recordSetRoute ~> check {
|
||||
status shouldBe StatusCodes.OK
|
||||
val resultRs = responseAs[ListGlobalRecordSetsResponse]
|
||||
(resultRs.recordSets.map(_.id) should contain)
|
||||
.only(rs2.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"GET recordsets by zone" should {
|
||||
|
@ -36,5 +36,6 @@ case class ListRecordSetResults(
|
||||
maxItems: Option[Int] = None,
|
||||
recordNameFilter: Option[String] = None,
|
||||
recordTypeFilter: Option[Set[RecordType]] = None,
|
||||
recordOwnerGroupFilter: Option[String] = None,
|
||||
nameSort: NameSort
|
||||
)
|
||||
|
@ -31,6 +31,7 @@ trait RecordSetRepository extends Repository {
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort
|
||||
): IO[ListRecordSetResults]
|
||||
|
||||
|
@ -98,6 +98,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -112,6 +113,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -197,6 +199,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(1),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -215,6 +218,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(1),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -229,6 +233,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(1),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -248,6 +253,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(1),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -261,6 +267,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(2),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -279,6 +286,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(6),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -298,6 +306,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(6),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -314,6 +323,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = Some(7),
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -335,6 +345,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = Some("AAAA"),
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -353,6 +364,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = Some("A"),
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -373,6 +385,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = Some("Dummy"),
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
@ -426,6 +439,7 @@ class DynamoDBRecordSetRepositoryIntegrationSpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
recordOwnerGroupFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
)
|
||||
|
||||
|
@ -142,6 +142,7 @@ class DynamoDBRecordSetRepository private[repository] (
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort
|
||||
): IO[ListRecordSetResults] =
|
||||
monitor("repo.RecordSet.listRecordSets") {
|
||||
@ -195,6 +196,7 @@ class DynamoDBRecordSetRepository private[repository] (
|
||||
maxItems,
|
||||
recordNameFilter,
|
||||
recordTypeFilter,
|
||||
recordOwnerGroupFilter,
|
||||
nameSort
|
||||
)
|
||||
}
|
||||
|
@ -185,7 +185,8 @@ class DynamoDBRecordSetRepositorySpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
nameSort = NameSort.ASC,
|
||||
recordOwnerGroupFilter = None
|
||||
)
|
||||
.unsafeRunSync()
|
||||
|
||||
@ -209,7 +210,15 @@ class DynamoDBRecordSetRepositorySpec
|
||||
|
||||
val response =
|
||||
store
|
||||
.listRecordSets(Some(rsOk.zoneId), None, Some(3), None, None, NameSort.ASC)
|
||||
.listRecordSets(
|
||||
Some(rsOk.zoneId),
|
||||
None,
|
||||
Some(3),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
NameSort.ASC
|
||||
)
|
||||
.unsafeRunSync()
|
||||
verify(dynamoDBHelper).query(any[QueryRequest])
|
||||
|
||||
@ -227,7 +236,8 @@ class DynamoDBRecordSetRepositorySpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
nameSort = NameSort.ASC,
|
||||
recordOwnerGroupFilter = None
|
||||
)
|
||||
}
|
||||
|
||||
@ -240,7 +250,8 @@ class DynamoDBRecordSetRepositorySpec
|
||||
maxItems = None,
|
||||
recordNameFilter = None,
|
||||
recordTypeFilter = None,
|
||||
nameSort = NameSort.ASC
|
||||
nameSort = NameSort.ASC,
|
||||
recordOwnerGroupFilter = None
|
||||
)
|
||||
.unsafeRunSync()
|
||||
}
|
||||
|
@ -385,14 +385,14 @@ class MySqlRecordSetRepositoryIntegrationSpec
|
||||
"list record sets" should {
|
||||
"return all record sets in a zone when optional params are not set" in {
|
||||
val existing = insert(okZone, 10).map(_.recordSet)
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, None, None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
found.recordSets should contain theSameElementsAs existing.map(r => recordSetWithFQDN(r, okZone))
|
||||
}
|
||||
"return record sets after the startFrom when set" in {
|
||||
// load 5, start after the 3rd, we should get back the last two
|
||||
val existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name)
|
||||
val startFrom = Some(PagingKey.toNextId(existing(2), true))
|
||||
val found = repo.listRecordSets(Some(okZone.id), startFrom, None, None,None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), startFrom, None, None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
|
||||
(found.recordSets should contain).theSameElementsInOrderAs(existing.drop(3)
|
||||
.map(r => recordSetWithFQDN(r, okZone)))
|
||||
@ -401,7 +401,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
|
||||
// load 5, start after the 2nd, take 2, we should get back the 3rd and 4th
|
||||
val existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name)
|
||||
val startFrom = Some(PagingKey.toNextId(existing(1), true))
|
||||
val found = repo.listRecordSets(Some(okZone.id), startFrom, Some(2), None, None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), startFrom, Some(2), None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
|
||||
(found.recordSets should contain).theSameElementsInOrderAs(existing.slice(2, 4)
|
||||
.map(r => recordSetWithFQDN(r, okZone)))
|
||||
@ -421,7 +421,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
|
||||
|
||||
val startFrom = Some(PagingKey.toNextId(newRecordSets(1), true))
|
||||
val found = repo.listRecordSets(
|
||||
Some(okZone.id), startFrom, Some(3), Some("*z*"), None, NameSort.ASC
|
||||
Some(okZone.id), startFrom, Some(3), Some("*z*"), None, None, NameSort.ASC
|
||||
).unsafeRunSync()
|
||||
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
|
||||
}
|
||||
@ -437,7 +437,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
|
||||
val changes = newRecordSets.map(makeTestAddChange(_, okZone))
|
||||
insert(changes)
|
||||
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, Some(3), Some("aa*"), None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, Some(3), Some("aa*"), None, None, NameSort.ASC).unsafeRunSync()
|
||||
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
|
||||
}
|
||||
"return record sets using ends with wildcard" in {
|
||||
@ -452,7 +452,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
|
||||
val changes = newRecordSets.map(makeTestAddChange(_, okZone))
|
||||
insert(changes)
|
||||
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, Some(3), Some("*b"), None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, Some(3), Some("*b"), None, None, NameSort.ASC).unsafeRunSync()
|
||||
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
|
||||
}
|
||||
"return record sets exact match with no wildcards" in {
|
||||
@ -468,35 +468,35 @@ class MySqlRecordSetRepositoryIntegrationSpec
|
||||
val changes = newRecordSets.map(makeTestAddChange(_, okZone))
|
||||
insert(changes)
|
||||
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, Some(3), Some("aaa"), None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, Some(3), Some("aaa"), None, None, NameSort.ASC).unsafeRunSync()
|
||||
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
|
||||
}
|
||||
"return select types of recordsets in a zone" in {
|
||||
insert(okZone, 10).map(_.recordSet)
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, None, None, Some(Set(CNAME)), NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, None, None, Some(Set(CNAME)), None, NameSort.ASC).unsafeRunSync()
|
||||
found.recordSets shouldBe List()
|
||||
found.recordTypeFilter shouldBe Some(Set(CNAME))
|
||||
}
|
||||
"return all recordsets in a zone in descending order" in {
|
||||
val existing = insert(okZone, 10).map(_.recordSet)
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, None, None, None, NameSort.DESC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(Some(okZone.id), None, None, None, None, None, NameSort.DESC).unsafeRunSync()
|
||||
found.recordSets should contain theSameElementsAs existing.map(r => recordSetWithFQDN(r, okZone))
|
||||
found.nameSort shouldBe NameSort.DESC
|
||||
}
|
||||
"pages through the list properly" in {
|
||||
// load 5 records, pages of 2, last page should have 1 result and no next id
|
||||
val existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name)
|
||||
val page1 = repo.listRecordSets(Some(okZone.id), None, Some(2), None, None, NameSort.ASC).unsafeRunSync()
|
||||
val page1 = repo.listRecordSets(Some(okZone.id), None, Some(2), None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
(page1.recordSets should contain).theSameElementsInOrderAs(existing.slice(0, 2)
|
||||
.map(r => recordSetWithFQDN(r, okZone)))
|
||||
page1.nextId shouldBe Some(PagingKey.toNextId(page1.recordSets(1), true))
|
||||
|
||||
val page2 = repo.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, NameSort.ASC).unsafeRunSync()
|
||||
val page2 = repo.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
(page2.recordSets should contain).theSameElementsInOrderAs(existing.slice(2, 4)
|
||||
.map(r => recordSetWithFQDN(r, okZone)))
|
||||
page2.nextId shouldBe Some(PagingKey.toNextId(page2.recordSets(1), true))
|
||||
|
||||
val page3 = repo.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, NameSort.ASC).unsafeRunSync()
|
||||
val page3 = repo.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
(page3.recordSets should contain).theSameElementsInOrderAs(existing.slice(4, 5)
|
||||
.map(r => recordSetWithFQDN(r, okZone)))
|
||||
page3.nextId shouldBe None
|
||||
@ -514,33 +514,33 @@ class MySqlRecordSetRepositoryIntegrationSpec
|
||||
insert(editedChanges)
|
||||
val existing = editedChanges.map(_.recordSet)
|
||||
|
||||
val page1 = repo.listRecordSets(Some(okZone.id), None, Some(2), None, None, NameSort.ASC).unsafeRunSync()
|
||||
val page1 = repo.listRecordSets(Some(okZone.id), None, Some(2), None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
(page1.recordSets should contain).theSameElementsInOrderAs(List(
|
||||
recordSetWithFQDN(existing.head, okZone),
|
||||
recordSetWithFQDN(existing(1), okZone)))
|
||||
page1.nextId shouldBe Some(PagingKey.toNextId(page1.recordSets.last, true))
|
||||
|
||||
val page2 = repo.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, NameSort.ASC).unsafeRunSync()
|
||||
val page2 = repo.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
(page2.recordSets should contain).theSameElementsInOrderAs(List(
|
||||
recordSetWithFQDN(existing(2), okZone), recordSetWithFQDN(existing(3), okZone)))
|
||||
page2.nextId shouldBe Some(PagingKey.toNextId(page2.recordSets.last, true))
|
||||
|
||||
val page3 = repo.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, NameSort.ASC).unsafeRunSync()
|
||||
val page3 = repo.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
(page3.recordSets should contain).theSameElementsInOrderAs(List(recordSetWithFQDN(existing(4), okZone)))
|
||||
page3.nextId shouldBe None
|
||||
}
|
||||
"return applicable recordsets in ascending order when recordNameFilter is given" in {
|
||||
val existing = insert(okZone, 10).map(_.recordSet)
|
||||
val found = repo.listRecordSets(None, None, None, Some("*.ok*"), None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(None, None, None, Some("*.ok*"), None, None, NameSort.ASC).unsafeRunSync()
|
||||
found.recordSets should contain theSameElementsAs existing.map(r => recordSetWithFQDN(r, okZone))
|
||||
}
|
||||
"return applicable recordsets in descending order when recordNameFilter is given and name sort is descending" in {
|
||||
val existing = insert(okZone, 10).map(_.recordSet)
|
||||
val found = repo.listRecordSets(None, None, None, Some("*.ok*"), None, NameSort.DESC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(None, None, None, Some("*.ok*"), None, None, NameSort.DESC).unsafeRunSync()
|
||||
found.recordSets should contain theSameElementsAs existing.map(r => recordSetWithFQDN(r, okZone)).reverse
|
||||
}
|
||||
"return no recordsets when no zoneId or recordNameFilter are given" in {
|
||||
val found = repo.listRecordSets(None, None, None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
val found = repo.listRecordSets(None, None, None, None, None, None, NameSort.ASC).unsafeRunSync()
|
||||
found.recordSets shouldBe empty
|
||||
}
|
||||
}
|
||||
|
@ -176,16 +176,20 @@ class MySqlRecordSetRepository extends RecordSetRepository with Monitored {
|
||||
maxItems: Option[Int],
|
||||
recordNameFilter: Option[String],
|
||||
recordTypeFilter: Option[Set[RecordType]],
|
||||
recordOwnerGroupFilter: Option[String],
|
||||
nameSort: NameSort
|
||||
): IO[ListRecordSetResults] =
|
||||
monitor("repo.RecordSet.listRecordSets") {
|
||||
IO {
|
||||
DB.readOnly { implicit s =>
|
||||
val maxPlusOne = maxItems.map(_ + 1)
|
||||
|
||||
// setup optional filters
|
||||
val zoneAndNameFilters = (zoneId, recordNameFilter) match {
|
||||
case (Some(zId), Some(rName)) =>
|
||||
Some(s"""WHERE zone_id = '$zId' AND name LIKE '${rName.replace('*', '%')}' """)
|
||||
case (None, Some(fqdn)) => Some(s"""WHERE fqdn LIKE '${fqdn.replace('*', '%')}' """)
|
||||
case (Some(zId), None) => Some(s"""WHERE zone_id = '$zId' """)
|
||||
Some(s"zone_id = '$zId' AND name LIKE '${rName.replace('*', '%')}' ")
|
||||
case (None, Some(fqdn)) => Some(s"fqdn LIKE '${fqdn.replace('*', '%')}' ")
|
||||
case (Some(zId), None) => Some(s"zone_id = '$zId' ")
|
||||
case _ => None
|
||||
}
|
||||
|
||||
@ -196,40 +200,49 @@ class MySqlRecordSetRepository extends RecordSetRepository with Monitored {
|
||||
val sortBy = (searchByZone, nameSort) match {
|
||||
case (true, NameSort.DESC) =>
|
||||
pagingKey.as(
|
||||
"AND ((name <= {startFromName} AND type > {startFromType}) OR name < {startFromName})"
|
||||
"((name <= {startFromName} AND type > {startFromType}) OR name < {startFromName})"
|
||||
)
|
||||
case (false, NameSort.ASC) =>
|
||||
pagingKey.as(
|
||||
"AND ((fqdn >= {startFromName} AND type > {startFromType}) OR fqdn > {startFromName})"
|
||||
"((fqdn >= {startFromName} AND type > {startFromType}) OR fqdn > {startFromName})"
|
||||
)
|
||||
case (false, NameSort.DESC) =>
|
||||
pagingKey.as(
|
||||
"AND ((fqdn <= {startFromName} AND type > {startFromType}) OR fqdn < {startFromName})"
|
||||
"((fqdn <= {startFromName} AND type > {startFromType}) OR fqdn < {startFromName})"
|
||||
)
|
||||
case _ =>
|
||||
pagingKey.as(
|
||||
"AND ((name >= {startFromName} AND type > {startFromType}) OR name > {startFromName})"
|
||||
"((name >= {startFromName} AND type > {startFromType}) OR name > {startFromName})"
|
||||
)
|
||||
}
|
||||
|
||||
val typeFilter = recordTypeFilter.map { t =>
|
||||
val list = t.map(fromRecordType).mkString(",")
|
||||
s"""AND type IN ($list)"""
|
||||
s"type IN ($list)"
|
||||
}
|
||||
|
||||
val maxPlusOne = maxItems.map(_ + 1)
|
||||
val ownerGroupFilter =
|
||||
recordOwnerGroupFilter.map(owner => s"owner_group_id = '$owner' ")
|
||||
|
||||
val opts = (zoneAndNameFilters ++ sortBy ++ typeFilter ++
|
||||
Some(s"""ORDER BY fqdn ${nameSort.toString}, type ASC""") ++
|
||||
maxPlusOne.as("LIMIT {maxItems}")).toList.mkString(" ")
|
||||
val opts =
|
||||
(zoneAndNameFilters ++ sortBy ++ typeFilter ++ ownerGroupFilter).toList
|
||||
|
||||
val qualifiers = new StringBuilder()
|
||||
qualifiers.append(s" ORDER BY fqdn ${nameSort.toString}, type ASC ")
|
||||
maxPlusOne.foreach(limit => qualifiers.append(s"LIMIT $limit"))
|
||||
|
||||
val params = (pagingKey.map(pk => 'startFromName -> pk.recordName) ++
|
||||
pagingKey.map(pk => 'startFromType -> pk.recordType) ++
|
||||
maxPlusOne.map(m => 'maxItems -> m)).toSeq
|
||||
pagingKey.map(pk => 'startFromType -> pk.recordType)).toSeq
|
||||
|
||||
val query = "SELECT data, fqdn FROM recordset " + opts
|
||||
// construct query
|
||||
val query = new StringBuilder()
|
||||
query.append("SELECT data, fqdn FROM recordset")
|
||||
if (opts.nonEmpty) {
|
||||
query.append(" WHERE ").append(opts.mkString(" AND "))
|
||||
}
|
||||
query.append(qualifiers)
|
||||
|
||||
val results = SQL(query)
|
||||
val results = SQL(query.toString())
|
||||
.bindByName(params: _*)
|
||||
.map(toRecordSet)
|
||||
.list()
|
||||
|
@ -85,11 +85,25 @@
|
||||
<input id="record-search-text" ng-model="query" type="text" class="form-control" placeholder="Record Name">
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<a class="record-type-filter-heading force-cursor dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">Filter By Record Type <i class="fa fa-chevron-down"></i></a>
|
||||
<ul class="dropdown-menu record-type-filters" aria-labelledby="dropdownMenu1">
|
||||
<li ng-repeat="recordType in readRecordTypes"><input type="checkbox" ng-checked="selectedRecordTypes.indexOf(recordType) != -1" ng-click="toggleCheckedRecordType(recordType)"> {{recordType}}</li>
|
||||
</ul>
|
||||
<a class="record-type-filter-heading force-cursor dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">Filters<i class="fa fa-chevron-down"></i></a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenu1">
|
||||
Record Types
|
||||
<div>
|
||||
<ul class="record-type-filters">
|
||||
<li ng-repeat="recordType in readRecordTypes" style="list-style-type:none;"><input type="checkbox" ng-checked="selectedRecordTypes.indexOf(recordType) != -1" ng-click="toggleCheckedRecordType(recordType)"> {{recordType}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
Record Owner Group
|
||||
<div>
|
||||
<select class="form-control" id="recordOwnerGroup" ng-model="ownerGroupFilter" ng-options="group.id as group.name for group in groups | orderBy: 'name'" ng-click="$event.stopPropagation();">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('recordset')
|
||||
.controller('RecordSetsController', function($scope, $log, $location, $timeout, recordsService, utilityService, pagingService){
|
||||
.controller('RecordSetsController', function($scope, $log, $location, $timeout, recordsService, utilityService, pagingService, groupsService){
|
||||
|
||||
$scope.recordSet = {};
|
||||
$scope.recordSetChanges = {};
|
||||
@ -27,6 +27,7 @@
|
||||
$scope.nameSortSymbol = "fa-chevron-up";
|
||||
$scope.readRecordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', "SOA", 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
|
||||
$scope.selectedRecordTypes = [];
|
||||
$scope.groups = [];
|
||||
|
||||
// paging status for recordsets
|
||||
var recordsPaging = pagingService.getNewPagingParams(100);
|
||||
@ -40,13 +41,21 @@
|
||||
}
|
||||
|
||||
return recordsService
|
||||
.listRecordSets(recordsPaging.maxItems, undefined, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort)
|
||||
.listRecordSets(recordsPaging.maxItems, undefined, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort, $scope.ownerGroupFilter)
|
||||
.then(success)
|
||||
.catch(function (error) {
|
||||
handleError(error, 'dnsChangesService::getRecordSet-failure');
|
||||
});
|
||||
};
|
||||
|
||||
groupsService.getGroups(true)
|
||||
.then(function (results) {
|
||||
$scope.groups = results['data']['groups'];
|
||||
})
|
||||
.catch(function (error) {
|
||||
handleError(error, 'groupsService::getGroups-failure');
|
||||
});
|
||||
|
||||
function handleError(error, type) {
|
||||
console.log(error);
|
||||
var alert = utilityService.failure(error, type);
|
||||
@ -106,7 +115,7 @@
|
||||
$scope.prevPage = function() {
|
||||
var startFrom = pagingService.getPrevStartFrom(recordsPaging);
|
||||
return recordsService
|
||||
.listRecordSets(recordsPaging.maxItems, startFrom, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort)
|
||||
.listRecordSets(recordsPaging.maxItems, startFrom, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort, $scope.recordOwnerGroupFilter)
|
||||
.then(function(response) {
|
||||
recordsPaging = pagingService.prevPageUpdate(response.data.nextId, recordsPaging);
|
||||
updateRecordDisplay(response.data.recordSets);
|
||||
@ -118,7 +127,7 @@
|
||||
|
||||
$scope.nextPage = function() {
|
||||
return recordsService
|
||||
.listRecordSets(recordsPaging.maxItems, recordsPaging.next, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort)
|
||||
.listRecordSets(recordsPaging.maxItems, recordsPaging.next, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort, $scope.recordOwnerGroupFilter)
|
||||
.then(function(response) {
|
||||
var recordSets = response.data.recordSets;
|
||||
recordsPaging = pagingService.nextPageUpdate(recordSets, response.data.nextId, recordsPaging);
|
||||
|
@ -19,22 +19,28 @@
|
||||
angular.module('service.records', [])
|
||||
.service('recordsService', function ($http, utilityService) {
|
||||
|
||||
this.listRecordSets = function (limit, startFrom, nameFilter, typeFilter, nameSort) {
|
||||
this.listRecordSets = function (limit, startFrom, nameFilter, typeFilter, nameSort, ownerGroupFilter) {
|
||||
if (typeFilter == "") {
|
||||
typeFilter = null;
|
||||
}
|
||||
if (nameSort == "") {
|
||||
nameSort = null;
|
||||
}
|
||||
|
||||
if (ownerGroupFilter == "") {
|
||||
ownerGroupFilter = null;
|
||||
}
|
||||
|
||||
var params = {
|
||||
"maxItems": limit,
|
||||
"startFrom": startFrom,
|
||||
"recordNameFilter": nameFilter,
|
||||
"recordTypeFilter": typeFilter,
|
||||
"nameSort": nameSort
|
||||
"nameSort": nameSort,
|
||||
"recordOwnerGroupFilter": ownerGroupFilter
|
||||
};
|
||||
var url = utilityService.urlBuilder("/api/recordsets", params);
|
||||
return $http.get(url);
|
||||
return $http.get(url)
|
||||
};
|
||||
|
||||
this.listRecordSetsByZone = function (id, limit, startFrom, nameFilter, typeFilter, nameSort) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user