diff --git a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala index cf1e4f3ae..f5e1b2891 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala @@ -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 ) diff --git a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala index 6fa280708..92f2b6933 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala @@ -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] diff --git a/modules/api/src/main/scala/vinyldns/api/domain/zone/ZoneViewLoader.scala b/modules/api/src/main/scala/vinyldns/api/domain/zone/ZoneViewLoader.scala index 090abcd0d..6b52ba424 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/zone/ZoneViewLoader.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/zone/ZoneViewLoader.scala @@ -108,6 +108,7 @@ case class VinylDNSZoneViewLoader(zone: Zone, recordSetRepository: RecordSetRepo maxItems = None, recordNameFilter = None, recordTypeFilter = None, + recordOwnerGroupFilter = None, nameSort = NameSort.ASC ) .map { result => diff --git a/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala b/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala index e1c60a463..918f55573 100644 --- a/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala +++ b/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala @@ -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), _ ) diff --git a/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala index 409782653..5076dbe0a 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala @@ -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 ) diff --git a/modules/api/src/test/scala/vinyldns/api/domain/zone/ZoneViewLoaderSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/zone/ZoneViewLoaderSpec.scala index 2e5ab6662..08cfb8069 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/zone/ZoneViewLoaderSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/zone/ZoneViewLoaderSpec.scala @@ -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] ) diff --git a/modules/api/src/test/scala/vinyldns/api/engine/ZoneSyncHandlerSpec.scala b/modules/api/src/test/scala/vinyldns/api/engine/ZoneSyncHandlerSpec.scala index 9ce06b9b9..998af86a7 100644 --- a/modules/api/src/test/scala/vinyldns/api/engine/ZoneSyncHandlerSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/engine/ZoneSyncHandlerSpec.scala @@ -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]) diff --git a/modules/api/src/test/scala/vinyldns/api/repository/EmptyRepositories.scala b/modules/api/src/test/scala/vinyldns/api/repository/EmptyRepositories.scala index c0267fb14..f448464ba 100644 --- a/modules/api/src/test/scala/vinyldns/api/repository/EmptyRepositories.scala +++ b/modules/api/src/test/scala/vinyldns/api/repository/EmptyRepositories.scala @@ -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)) diff --git a/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala b/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala index 3a6183077..0317fc39a 100644 --- a/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala @@ -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 { diff --git a/modules/core/src/main/scala/vinyldns/core/domain/record/ListRecordSetResults.scala b/modules/core/src/main/scala/vinyldns/core/domain/record/ListRecordSetResults.scala index d1ed3d078..54266341b 100644 --- a/modules/core/src/main/scala/vinyldns/core/domain/record/ListRecordSetResults.scala +++ b/modules/core/src/main/scala/vinyldns/core/domain/record/ListRecordSetResults.scala @@ -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 ) diff --git a/modules/core/src/main/scala/vinyldns/core/domain/record/RecordSetRepository.scala b/modules/core/src/main/scala/vinyldns/core/domain/record/RecordSetRepository.scala index cf8b0d2e9..bf0b50479 100644 --- a/modules/core/src/main/scala/vinyldns/core/domain/record/RecordSetRepository.scala +++ b/modules/core/src/main/scala/vinyldns/core/domain/record/RecordSetRepository.scala @@ -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] diff --git a/modules/dynamodb/src/it/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositoryIntegrationSpec.scala b/modules/dynamodb/src/it/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositoryIntegrationSpec.scala index ba3af6d11..d095d04ca 100644 --- a/modules/dynamodb/src/it/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositoryIntegrationSpec.scala +++ b/modules/dynamodb/src/it/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositoryIntegrationSpec.scala @@ -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 ) diff --git a/modules/dynamodb/src/main/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepository.scala b/modules/dynamodb/src/main/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepository.scala index 4c61e8ab0..d1887053c 100644 --- a/modules/dynamodb/src/main/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepository.scala +++ b/modules/dynamodb/src/main/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepository.scala @@ -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 ) } diff --git a/modules/dynamodb/src/test/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositorySpec.scala b/modules/dynamodb/src/test/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositorySpec.scala index 8e6fd9a68..4486fee94 100644 --- a/modules/dynamodb/src/test/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositorySpec.scala +++ b/modules/dynamodb/src/test/scala/vinyldns/dynamodb/repository/DynamoDBRecordSetRepositorySpec.scala @@ -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() } diff --git a/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordSetRepositoryIntegrationSpec.scala b/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordSetRepositoryIntegrationSpec.scala index bf25ff23a..def262b20 100644 --- a/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordSetRepositoryIntegrationSpec.scala +++ b/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordSetRepositoryIntegrationSpec.scala @@ -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 } } diff --git a/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordSetRepository.scala b/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordSetRepository.scala index f9e26a419..331130d53 100644 --- a/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordSetRepository.scala +++ b/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordSetRepository.scala @@ -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() diff --git a/modules/portal/app/views/recordsets/recordSets.scala.html b/modules/portal/app/views/recordsets/recordSets.scala.html index 30625a153..e9e670beb 100644 --- a/modules/portal/app/views/recordsets/recordSets.scala.html +++ b/modules/portal/app/views/recordsets/recordSets.scala.html @@ -85,11 +85,25 @@ + diff --git a/modules/portal/public/lib/recordset/recordsets.controller.js b/modules/portal/public/lib/recordset/recordsets.controller.js index e621b52ac..2b2d4d866 100644 --- a/modules/portal/public/lib/recordset/recordsets.controller.js +++ b/modules/portal/public/lib/recordset/recordsets.controller.js @@ -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); diff --git a/modules/portal/public/lib/services/records/service.records.js b/modules/portal/public/lib/services/records/service.records.js index d7b029752..7275c87e4 100644 --- a/modules/portal/public/lib/services/records/service.records.js +++ b/modules/portal/public/lib/services/records/service.records.js @@ -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) {