2
0
mirror of https://github.com/VinylDNS/vinyldns synced 2025-08-31 14:25:30 +00:00

Merge pull request #1180 from Jay07GIT/sort_recordtype

Added record type sort in manage zones tab
This commit is contained in:
Nicholas Spadaccino
2023-07-12 16:36:22 -04:00
committed by GitHub
24 changed files with 368 additions and 110 deletions

View File

@@ -30,7 +30,7 @@ import vinyldns.api.engine.ZoneSyncHandler
import vinyldns.api.{MySqlApiIntegrationSpec, ResultHelpers}
import vinyldns.core.TestRecordSetData._
import vinyldns.core.domain.backend.{Backend, BackendResolver}
import vinyldns.core.domain.record.{NameSort, RecordType}
import vinyldns.core.domain.record.{NameSort, RecordType, RecordTypeSort}
import vinyldns.core.domain.zone.{Zone, ZoneChange, ZoneChangeType}
import vinyldns.core.health.HealthCheck.HealthCheck
import vinyldns.route53.backend.{Route53Backend, Route53BackendConfig}
@@ -120,7 +120,7 @@ class Route53ApiIntegrationSpec
// We should have both the record we created above as well as at least one NS record
val results = recordSetRepository
.listRecordSets(Some(testZone.id), None, None, None, None, None, NameSort.ASC)
.listRecordSets(Some(testZone.id), None, None, None, None, None, NameSort.ASC, RecordTypeSort.ASC)
.unsafeRunSync()
results.recordSets.map(_.typ).distinct should contain theSameElementsAs List(
rsOk.typ,

View File

@@ -19,7 +19,7 @@ package vinyldns.api.domain.record
import cats.effect.IO
import org.slf4j.LoggerFactory
import scalikejdbc.DB
import vinyldns.core.domain.record.{NameSort, ListRecordSetResults, RecordSetCacheRepository, RecordSetRepository}
import vinyldns.core.domain.record.{ListRecordSetResults, NameSort, RecordSetCacheRepository, RecordSetRepository, RecordTypeSort}
import vinyldns.mysql.TransactionProvider
@@ -30,7 +30,7 @@ class RecordSetCacheService(recordSetRepository: RecordSetRepository,
final def populateRecordSetCache(nextId: Option[String] = None): IO[ListRecordSetResults] = {
logger.info(s"Populating recordset data. Starting at $nextId")
for {
result <- recordSetRepository.listRecordSets(None, nextId, Some(1000), None, None, None, NameSort.ASC)
result <- recordSetRepository.listRecordSets(None, nextId, Some(1000), None, None, None, NameSort.ASC, RecordTypeSort.ASC)
_ <- executeWithinTransaction { db: DB =>
IO {

View File

@@ -35,6 +35,7 @@ import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.DomainHelpers.ensureTrailingDot
import vinyldns.core.domain.backend.{Backend, BackendResolver}
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import scala.util.matching.Regex
@@ -408,7 +409,8 @@ class RecordSetService(
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] =
for {
_ <- validRecordNameFilterLength(recordNameFilter).toResult
@@ -421,7 +423,8 @@ class RecordSetService(
Some(formattedRecordNameFilter),
recordTypeFilter,
recordOwnerGroupFilter,
nameSort
nameSort,
recordTypeSort
)
.toResult[ListRecordSetResults]
rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet
@@ -459,7 +462,8 @@ class RecordSetService(
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] = {
for {
_ <- validRecordNameFilterLength(recordNameFilter).toResult
@@ -484,7 +488,8 @@ class RecordSetService(
Some(formattedRecordNameFilter),
recordTypeFilter,
recordOwnerGroupFilter,
nameSort
nameSort,
recordTypeSort
).toResult[ListRecordSetResults]
}
rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet
@@ -512,7 +517,8 @@ class RecordSetService(
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListRecordSetsByZoneResponse] =
for {
zone <- getZone(zoneId)
@@ -525,7 +531,8 @@ class RecordSetService(
recordNameFilter,
recordTypeFilter,
recordOwnerGroupFilter,
nameSort
nameSort,
recordTypeSort
)
.toResult[ListRecordSetResults]
rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet
@@ -540,7 +547,8 @@ class RecordSetService(
recordSetResults.recordNameFilter,
recordSetResults.recordTypeFilter,
recordSetResults.recordOwnerGroupFilter,
recordSetResults.nameSort
recordSetResults.nameSort,
recordSetResults.recordTypeSort
)
def getRecordSetChange(

View File

@@ -23,6 +23,7 @@ import vinyldns.core.domain.zone.ZoneCommandResult
import vinyldns.api.route.{ListGlobalRecordSetsResponse, ListRecordSetsByZoneResponse}
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.domain.record.{RecordSet, RecordSetChange}
trait RecordSetServiceAlgebra {
@@ -54,7 +55,8 @@ trait RecordSetServiceAlgebra {
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupId: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse]
/**
@@ -76,7 +78,8 @@ trait RecordSetServiceAlgebra {
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupId: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse]
def listRecordSetsByZone(
@@ -87,7 +90,8 @@ trait RecordSetServiceAlgebra {
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupId: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListRecordSetsByZoneResponse]
def getRecordSetChange(

View File

@@ -20,7 +20,7 @@ import cats.effect._
import org.slf4j.LoggerFactory
import vinyldns.api.backend.dns.DnsConversions
import vinyldns.core.domain.backend.Backend
import vinyldns.core.domain.record.{NameSort, RecordSetCacheRepository, RecordSetRepository}
import vinyldns.core.domain.record.{NameSort, RecordSetCacheRepository, RecordSetRepository, RecordTypeSort}
import vinyldns.core.domain.zone.Zone
import vinyldns.core.route.Monitored
@@ -69,7 +69,8 @@ case class VinylDNSZoneViewLoader(
recordNameFilter = None,
recordTypeFilter = None,
recordOwnerGroupFilter = None,
nameSort = NameSort.ASC
nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
)
.map { result =>
VinylDNSZoneViewLoader.logger.info(

View File

@@ -54,6 +54,7 @@ trait DnsJsonProtocol extends JsonValidation {
JsonEnumV(ZoneChangeType),
JsonEnumV(RecordSetChangeType),
JsonEnumV(NameSort),
JsonEnumV(RecordTypeSort),
ASerializer,
AAAASerializer,
CNAMESerializer,

View File

@@ -26,7 +26,8 @@ import vinyldns.api.config.LimitsConfig
import vinyldns.api.domain.zone._
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.{NameSort, RecordSet, RecordType}
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.domain.record.{NameSort, RecordSet, RecordType, RecordTypeSort}
import vinyldns.core.domain.zone.ZoneCommandResult
import scala.concurrent.duration._
@@ -52,7 +53,8 @@ case class ListRecordSetsByZoneResponse(
recordNameFilter: Option[String] = None,
recordTypeFilter: Option[Set[RecordType]] = None,
recordOwnerGroupFilter: Option[String] = None,
nameSort: NameSort
nameSort: NameSort,
recordTypeSort: RecordTypeSort
)
class RecordSetRoute(
@@ -100,7 +102,8 @@ class RecordSetRoute(
"recordNameFilter".?,
"recordTypeFilter".?,
"recordOwnerGroupFilter".?,
"nameSort".as[String].?("ASC")
"nameSort".as[String].?("ASC"),
"recordTypeSort".as[String].?("None")
) {
(
startFrom: Option[String],
@@ -108,7 +111,8 @@ class RecordSetRoute(
recordNameFilter: Option[String],
recordTypeFilter: Option[String],
recordOwnerGroupFilter: Option[String],
nameSort: String
nameSort: String,
recordTypeSort: String
) =>
val convertedRecordTypeFilter = convertRecordTypeFilter(recordTypeFilter)
handleRejections(invalidQueryHandler) {
@@ -126,8 +130,9 @@ class RecordSetRoute(
convertedRecordTypeFilter,
recordOwnerGroupFilter,
NameSort.find(nameSort),
_
)
_,
RecordTypeSort.find(recordTypeSort),
)
) { rsResponse =>
complete(StatusCodes.OK, rsResponse)
}
@@ -144,7 +149,8 @@ class RecordSetRoute(
"recordNameFilter".as[String],
"recordTypeFilter".?,
"recordOwnerGroupFilter".?,
"nameSort".as[String].?("ASC")
"nameSort".as[String].?("ASC"),
"recordTypeSort".as[String].?("NONE")
) {
(
startFrom: Option[String],
@@ -152,7 +158,8 @@ class RecordSetRoute(
recordNameFilter: String,
recordTypeFilter: Option[String],
recordOwnerGroupFilter: Option[String],
nameSort: String
nameSort: String,
recordTypeSort: String
) =>
val convertedRecordTypeFilter = convertRecordTypeFilter(recordTypeFilter)
handleRejections(invalidQueryHandler) {
@@ -169,7 +176,8 @@ class RecordSetRoute(
convertedRecordTypeFilter,
recordOwnerGroupFilter,
NameSort.find(nameSort),
_
_,
RecordTypeSort.find(recordTypeSort)
)
) { rsResponse =>
complete(StatusCodes.OK, rsResponse)

View File

@@ -1679,7 +1679,8 @@ class RecordSetServiceSpec
List(sharedZoneRecord),
recordNameFilter = Some("aaaa*"),
nameSort = NameSort.ASC,
recordOwnerGroupFilter = Some("owner group id")
recordOwnerGroupFilter = Some("owner group id") ,
recordTypeSort = RecordTypeSort.NONE
)
)
).when(mockRecordRepo)
@@ -1690,7 +1691,8 @@ class RecordSetServiceSpec
recordNameFilter = any[Option[String]],
recordTypeFilter = any[Option[Set[RecordType.RecordType]]],
recordOwnerGroupFilter = any[Option[String]],
nameSort = any[NameSort.NameSort]
nameSort = any[NameSort.NameSort],
recordTypeSort = any[RecordTypeSort.RecordTypeSort]
)
val result: ListGlobalRecordSetsResponse =
@@ -1702,7 +1704,8 @@ class RecordSetServiceSpec
recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC,
authPrincipal = sharedAuth
authPrincipal = sharedAuth,
recordTypeSort = RecordTypeSort.ASC
)
.value.unsafeRunSync().toOption.get
@@ -1727,7 +1730,8 @@ class RecordSetServiceSpec
recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC,
authPrincipal = okAuth
authPrincipal = okAuth,
recordTypeSort = RecordTypeSort.ASC
)
.value.unsafeRunSync().swap.toOption.get
@@ -1751,7 +1755,8 @@ class RecordSetServiceSpec
List(sharedZoneRecord),
recordNameFilter = Some("aaaa*"),
nameSort = NameSort.ASC,
recordOwnerGroupFilter = Some("owner group id")
recordOwnerGroupFilter = Some("owner group id"),
recordTypeSort = RecordTypeSort.NONE
)
)
).when(mockRecordDataRepo)
@@ -1774,7 +1779,8 @@ class RecordSetServiceSpec
recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC,
authPrincipal = sharedAuth
authPrincipal = sharedAuth,
recordTypeSort = RecordTypeSort.ASC
)
.value.unsafeRunSync().toOption.get
@@ -1799,7 +1805,8 @@ class RecordSetServiceSpec
recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC,
authPrincipal = okAuth
authPrincipal = okAuth,
recordTypeSort = RecordTypeSort.ASC
)
.value.unsafeRunSync().swap.toOption.get
@@ -1818,7 +1825,8 @@ class RecordSetServiceSpec
IO.pure(
ListRecordSetResults(
List(sharedZoneRecord, sharedZoneRecordNotFoundOwnerGroup),
nameSort = NameSort.ASC
nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
)
)
).when(mockRecordRepo)
@@ -1829,7 +1837,8 @@ class RecordSetServiceSpec
recordNameFilter = None,
recordTypeFilter = None,
recordOwnerGroupFilter = None,
nameSort = NameSort.ASC
nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
)
val result: ListRecordSetsByZoneResponse =
@@ -1842,7 +1851,8 @@ class RecordSetServiceSpec
authPrincipal = sharedAuth,
recordTypeFilter = None,
recordOwnerGroupFilter = None,
nameSort = NameSort.ASC
nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
)
.value.unsafeRunSync().toOption.get
@@ -1863,7 +1873,7 @@ class RecordSetServiceSpec
.when(mockGroupRepo)
.getGroups(Set())
doReturn(IO.pure(ListRecordSetResults(List(aaaa), nameSort = NameSort.ASC)))
doReturn(IO.pure(ListRecordSetResults(List(aaaa), nameSort = NameSort.ASC, recordTypeSort = RecordTypeSort.NONE)))
.when(mockRecordRepo)
.listRecordSets(
zoneId = Some(okZone.id),
@@ -1872,7 +1882,8 @@ class RecordSetServiceSpec
recordNameFilter = None,
recordTypeFilter = None,
recordOwnerGroupFilter = None,
nameSort = NameSort.ASC
nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
)
val result: ListRecordSetsByZoneResponse =
@@ -1885,7 +1896,8 @@ class RecordSetServiceSpec
recordTypeFilter = None,
recordOwnerGroupFilter = None,
nameSort = NameSort.ASC,
authPrincipal = AuthPrincipal(okAuth.signedInUser.copy(isSupport = true), Seq.empty)
authPrincipal = AuthPrincipal(okAuth.signedInUser.copy(isSupport = true), Seq.empty),
recordTypeSort = RecordTypeSort.ASC
)
.value.unsafeRunSync().toOption.get
@@ -1904,7 +1916,8 @@ class RecordSetServiceSpec
recordTypeFilter = None,
recordOwnerGroupFilter = None,
nameSort = NameSort.ASC,
authPrincipal = okAuth
authPrincipal = okAuth,
recordTypeSort = RecordTypeSort.ASC
)
.value.unsafeRunSync().swap.toOption.get

View File

@@ -36,6 +36,7 @@ import vinyldns.core.domain.{Encrypted, Fqdn}
import vinyldns.core.domain.backend.{Backend, BackendResolver}
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.domain.zone.{Zone, ZoneConnection, ZoneStatus}
class ZoneViewLoaderSpec extends AnyWordSpec with Matchers with MockitoSugar with DnsConversions {
@@ -95,7 +96,7 @@ class ZoneViewLoaderSpec extends AnyWordSpec with Matchers with MockitoSugar wit
val mockRecordSetDataRepo = mock[RecordSetCacheRepository]
doReturn(IO(ListRecordSetResults(records, None, None, None, None, None, None, NameSort.ASC)))
doReturn(IO(ListRecordSetResults(records, None, None, None, None, None, None, NameSort.ASC, RecordTypeSort.NONE)))
.when(mockRecordSetRepo)
.listRecordSets(
any[Option[String]],
@@ -104,7 +105,8 @@ class ZoneViewLoaderSpec extends AnyWordSpec with Matchers with MockitoSugar wit
any[Option[String]],
any[Option[Set[RecordType]]],
any[Option[String]],
any[NameSort]
any[NameSort],
any[RecordTypeSort]
)
val underTest = VinylDNSZoneViewLoader(testZone, mockRecordSetRepo, mockRecordSetDataRepo)

View File

@@ -40,6 +40,7 @@ import vinyldns.core.domain.zone._
import cats.syntax.all._
import org.slf4j.{Logger, LoggerFactory}
import vinyldns.api.engine.ZoneSyncHandler.{monitor, time}
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.mysql.TransactionProvider
class ZoneSyncHandlerSpec
@@ -311,7 +312,7 @@ class ZoneSyncHandlerSpec
)
doReturn(
IO(ListRecordSetResults(List(testRecord1), None, None, None, None, None, None, NameSort.ASC))
IO(ListRecordSetResults(List(testRecord1), None, None, None, None, None, None, NameSort.ASC, recordTypeSort = RecordTypeSort.NONE))
).when(recordSetRepo)
.listRecordSets(
any[Option[String]],
@@ -320,7 +321,8 @@ class ZoneSyncHandlerSpec
any[Option[String]],
any[Option[Set[RecordType]]],
any[Option[String]],
any[NameSort]
any[NameSort],
any[RecordTypeSort],
)
doReturn(IO(testChangeSet)).when(recordSetRepo).apply(any[DB], any[ChangeSet])

View File

@@ -19,11 +19,12 @@ package vinyldns.api.repository
import vinyldns.core.domain.auth.AuthPrincipal
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record._
import vinyldns.core.domain.zone.{Zone, ZoneRepository, ListZonesResults}
import vinyldns.core.domain.zone.{ListZonesResults, Zone, ZoneRepository}
import cats.effect._
import scalikejdbc._
import vinyldns.core.domain.membership._
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.domain.zone.ZoneRepository.DuplicateZoneError
// Empty implementations let our other test classes just edit with the methods they need
@@ -42,9 +43,10 @@ trait EmptyRecordSetRepo extends RecordSetRepository {
recordNameFilter: Option[String],
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort
nameSort: NameSort,
recordTypeSort: RecordTypeSort
): IO[ListRecordSetResults] =
IO.pure(ListRecordSetResults(nameSort = nameSort))
IO.pure(ListRecordSetResults(nameSort = nameSort,recordTypeSort=recordTypeSort))
def getRecordSets(zoneId: String, name: String, typ: RecordType): IO[List[RecordSet]] =
@@ -73,7 +75,7 @@ trait EmptyRecordSetCacheRepo extends RecordSetCacheRepository {
recordOwnerGroupFilter: Option[String],
nameSort: NameSort
): IO[ListRecordSetResults] =
IO.pure(ListRecordSetResults(nameSort = nameSort))
IO.pure(ListRecordSetResults(nameSort = nameSort, recordTypeSort = RecordTypeSort.NONE))
}
trait EmptyZoneRepo extends ZoneRepository {

View File

@@ -37,6 +37,7 @@ import vinyldns.core.domain.auth.AuthPrincipal
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordSetChangeType.RecordSetChangeType
import vinyldns.core.domain.record.RecordType._
import vinyldns.core.domain.record.RecordTypeSort.{ASC, DESC, RecordTypeSort}
import vinyldns.core.domain.record._
import vinyldns.core.domain.zone._
@@ -529,7 +530,8 @@ class RecordSetRoutingSpec
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] = {
if (recordTypeFilter.contains(Set(CNAME))) {
Right(
@@ -587,7 +589,8 @@ class RecordSetRoutingSpec
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] = {
if (recordTypeFilter.contains(Set(CNAME))) {
Right(
@@ -637,10 +640,49 @@ class RecordSetRoutingSpec
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort,
authPrincipal: AuthPrincipal
authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListRecordSetsByZoneResponse] = {
zoneId match {
case zoneNotFound.id => Left(ZoneNotFoundError(s"$zoneId"))
// NameSort will be in ASC by default
case okZone.id if recordTypeSort==DESC =>
Right(
ListRecordSetsByZoneResponse(
List(
RecordSetListInfo(RecordSetInfo(soa, None), AccessLevel.Read),
RecordSetListInfo(RecordSetInfo(cname, None), AccessLevel.Read),
RecordSetListInfo(RecordSetInfo(aaaa, None), AccessLevel.Read)
),
startFrom,
None,
maxItems,
recordNameFilter,
recordTypeFilter,
None,
nameSort,
recordTypeSort
)
)
// NameSort will be in ASC by default
case okZone.id if recordTypeSort==ASC=>
Right(
ListRecordSetsByZoneResponse(
List(
RecordSetListInfo(RecordSetInfo(aaaa, None), AccessLevel.Read),
RecordSetListInfo(RecordSetInfo(cname, None), AccessLevel.Read),
RecordSetListInfo(RecordSetInfo(soa, None), AccessLevel.Read)
),
startFrom,
None,
maxItems,
recordNameFilter,
recordTypeFilter,
None,
nameSort,
recordTypeSort
)
)
case okZone.id if recordTypeFilter.contains(Set(CNAME)) =>
Right(
ListRecordSetsByZoneResponse(
@@ -653,7 +695,8 @@ class RecordSetRoutingSpec
recordNameFilter,
recordTypeFilter,
recordOwnerGroupFilter,
nameSort
nameSort,
recordTypeSort=RecordTypeSort.ASC
)
)
case okZone.id if recordTypeFilter.isEmpty =>
@@ -670,7 +713,8 @@ class RecordSetRoutingSpec
recordNameFilter,
recordTypeFilter,
None,
nameSort
nameSort,
recordTypeSort=RecordTypeSort.ASC
)
)
}
@@ -1082,6 +1126,38 @@ class RecordSetRoutingSpec
}
}
"return all recordSets types in descending order" in {
Get(s"/zones/${okZone.id}/recordsets?recordTypeSort=desc") ~> recordSetRoute ~> check {
status shouldBe StatusCodes.OK
val resultRs = responseAs[ListRecordSetsByZoneResponse]
(resultRs.recordSets.map(_.typ) shouldBe List(soa.typ, cname.typ, aaaa.typ))
}
}
"return all recordSets types in ascending order" in {
Get(s"/zones/${okZone.id}/recordsets?recordTypeSort=asc") ~> recordSetRoute ~> check {
status shouldBe StatusCodes.OK
val resultRs = responseAs[ListRecordSetsByZoneResponse]
(resultRs.recordSets.map(_.typ) shouldBe List(aaaa.typ, cname.typ, soa.typ))
}
}
"return all record name in ascending order when name and type sort simultaneously" in {
Get(s"/zones/${okZone.id}/recordsets?nameSort=desc&recordTypeSort=asc") ~> recordSetRoute ~> check {
status shouldBe StatusCodes.OK
val resultRs = responseAs[ListRecordSetsByZoneResponse]
(resultRs.recordSets.map(_.name) shouldBe List(aaaa.name, cname.name, soa.name))
}
}
"return recordsets of a specific type" in {
Get(s"/zones/${okZone.id}/recordsets?recordTypeFilter=cname") ~> recordSetRoute ~> check {
status shouldBe StatusCodes.OK

View File

@@ -18,6 +18,7 @@ package vinyldns.core.domain.record
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
object NameSort extends Enumeration {
type NameSort = Value
@@ -26,6 +27,19 @@ object NameSort extends Enumeration {
def find(value: String): Value = value.toUpperCase match {
case "DESC" => NameSort.DESC
case _ => NameSort.ASC
}
}
object RecordTypeSort extends Enumeration {
type RecordTypeSort = Value
val ASC, DESC, NONE = Value
def find(value: String): Value = value.toUpperCase match {
case "DESC" => RecordTypeSort.DESC
case "ASC" => RecordTypeSort.ASC
case _ => RecordTypeSort.NONE
}
}
@@ -37,5 +51,6 @@ case class ListRecordSetResults(
recordNameFilter: Option[String] = None,
recordTypeFilter: Option[Set[RecordType]] = None,
recordOwnerGroupFilter: Option[String] = None,
nameSort: NameSort
nameSort: NameSort,
recordTypeSort: RecordTypeSort
)

View File

@@ -20,6 +20,7 @@ import cats.effect._
import scalikejdbc.DB
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.repository.Repository
trait RecordSetRepository extends Repository {
@@ -33,7 +34,8 @@ trait RecordSetRepository extends Repository {
recordNameFilter: Option[String],
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort
nameSort: NameSort,
recordTypeSort: RecordTypeSort
): IO[ListRecordSetResults]
def getRecordSets(zoneId: String, name: String, typ: RecordType): IO[List[RecordSet]]

View File

@@ -419,7 +419,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
"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, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), None, None, None, None, None, NameSort.ASC, RecordTypeSort.ASC)
.unsafeRunSync()
found.recordSets should contain theSameElementsAs existing.map(
r => recordSetWithFQDN(r, okZone)
@@ -430,7 +430,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
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, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), startFrom, None, None, None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(found.recordSets should contain).theSameElementsInOrderAs(
@@ -444,7 +444,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
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, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), startFrom, Some(2), None, None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(found.recordSets should contain).theSameElementsInOrderAs(
@@ -475,7 +475,8 @@ class MySqlRecordSetRepositoryIntegrationSpec
Some("*z*"),
None,
None,
NameSort.ASC
NameSort.ASC,
RecordTypeSort.NONE
)
.unsafeRunSync()
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
@@ -493,7 +494,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
insert(changes)
val found = repo
.listRecordSets(Some(okZone.id), None, Some(3), Some("aa*"), None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), None, Some(3), Some("aa*"), None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
}
@@ -510,7 +511,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
insert(changes)
val found = repo
.listRecordSets(Some(okZone.id), None, Some(3), Some("*b"), None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), None, Some(3), Some("*b"), None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
}
@@ -528,14 +529,14 @@ class MySqlRecordSetRepositoryIntegrationSpec
insert(changes)
val found = repo
.listRecordSets(Some(okZone.id), None, Some(3), Some("aaa"), None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), None, Some(3), Some("aaa"), None, None, NameSort.ASC, RecordTypeSort.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)), None, NameSort.ASC)
.listRecordSets(Some(okZone.id), None, None, None, Some(Set(CNAME)), None, NameSort.ASC,RecordTypeSort.ASC)
.unsafeRunSync()
found.recordSets shouldBe List()
found.recordTypeFilter shouldBe Some(Set(CNAME))
@@ -543,18 +544,30 @@ class MySqlRecordSetRepositoryIntegrationSpec
"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, None, NameSort.DESC)
.listRecordSets(Some(okZone.id), None, None, None, None, None, NameSort.DESC, RecordTypeSort.NONE)
.unsafeRunSync()
found.recordSets should contain theSameElementsAs existing.map(
r => recordSetWithFQDN(r, okZone)
)
found.nameSort shouldBe NameSort.DESC
}
"return all recordsets record type 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, None, NameSort.ASC, RecordTypeSort.DESC)
.unsafeRunSync()
found.recordSets should contain theSameElementsAs existing.map(
r => recordSetWithFQDN(r, okZone)
)
found.recordTypeSort shouldBe RecordTypeSort.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, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), None, Some(2), None, None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(page1.recordSets should contain).theSameElementsInOrderAs(
existing
@@ -564,7 +577,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
page1.nextId shouldBe Some(PagingKey.toNextId(page1.recordSets(1), true))
val page2 = repo
.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(page2.recordSets should contain).theSameElementsInOrderAs(
existing
@@ -574,7 +587,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
page2.nextId shouldBe Some(PagingKey.toNextId(page2.recordSets(1), true))
val page3 = repo
.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, None, NameSort.ASC, RecordTypeSort.ASC)
.unsafeRunSync()
(page3.recordSets should contain).theSameElementsInOrderAs(
existing
@@ -597,7 +610,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
val existing = editedChanges.map(_.recordSet)
val page1 = repo
.listRecordSets(Some(okZone.id), None, Some(2), None, None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), None, Some(2), None, None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(page1.recordSets should contain).theSameElementsInOrderAs(
List(recordSetWithFQDN(existing.head, okZone), recordSetWithFQDN(existing(1), okZone))
@@ -605,7 +618,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
page1.nextId shouldBe Some(PagingKey.toNextId(page1.recordSets.last, true))
val page2 = repo
.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), page1.nextId, Some(2), None, None, None, NameSort.ASC, RecordTypeSort.NONE)
.unsafeRunSync()
(page2.recordSets should contain).theSameElementsInOrderAs(
List(recordSetWithFQDN(existing(2), okZone), recordSetWithFQDN(existing(3), okZone))
@@ -613,16 +626,16 @@ class MySqlRecordSetRepositoryIntegrationSpec
page2.nextId shouldBe Some(PagingKey.toNextId(page2.recordSets.last, true))
val page3 = repo
.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, None, NameSort.ASC)
.listRecordSets(Some(okZone.id), page2.nextId, Some(2), None, None, None, NameSort.ASC, RecordTypeSort.NONE)
.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 {
"return applicable recordsets in ascending order respect to record type when recordNameFilter is given and record type sort is ascending" in {
val existing = insert(okZone, 10).map(_.recordSet)
val found = repo
.listRecordSets(None, None, None, Some("*.ok*"), None, None, NameSort.ASC)
.listRecordSets(None, None, None, Some("*.ok*"), None, None, NameSort.ASC, RecordTypeSort.ASC)
.unsafeRunSync()
found.recordSets should contain theSameElementsAs existing.map(
r => recordSetWithFQDN(r, okZone)
@@ -631,7 +644,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
"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, None, NameSort.DESC)
.listRecordSets(None, None, None, Some("*.ok*"), None, None, NameSort.DESC, RecordTypeSort.ASC)
.unsafeRunSync()
found.recordSets should contain theSameElementsAs existing
.map(r => recordSetWithFQDN(r, okZone))
@@ -639,7 +652,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
}
"return no recordsets when no zoneId or recordNameFilter are given" in {
val found =
repo.listRecordSets(None, None, None, None, None, None, NameSort.ASC).unsafeRunSync()
repo.listRecordSets(None, None, None, None, None, None, NameSort.ASC, RecordTypeSort.ASC).unsafeRunSync()
found.recordSets shouldBe empty
}
}

View File

@@ -396,7 +396,8 @@ class MySqlRecordSetCacheRepository
maxItems = maxItems,
recordNameFilter = recordNameFilter,
recordTypeFilter = recordTypeFilter,
nameSort = nameSort)
nameSort = nameSort,
recordTypeSort = RecordTypeSort.NONE)
}
}
}

View File

@@ -20,9 +20,10 @@ import cats.effect._
import cats.implicits._
import org.slf4j.LoggerFactory
import scalikejdbc._
import vinyldns.core.domain.record.NameSort.{ASC, NameSort}
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record._
import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.protobuf.ProtobufConversions
import vinyldns.core.route.Monitored
import vinyldns.proto.VinylDNSProto
@@ -176,7 +177,8 @@ class MySqlRecordSetRepository extends RecordSetRepository with Monitored {
recordNameFilter: Option[String],
recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String],
nameSort: NameSort
nameSort: NameSort,
recordTypeSort: RecordTypeSort,
): IO[ListRecordSetResults] =
monitor("repo.RecordSet.listRecordSets") {
IO {
@@ -226,11 +228,16 @@ class MySqlRecordSetRepository extends RecordSetRepository with Monitored {
val opts =
(zoneAndNameFilters ++ sortBy ++ typeFilter ++ ownerGroupFilter).toList
val qualifiers = if (nameSort == ASC) {
sqls"ORDER BY fqdn ASC, type ASC "
val nameSortQualifiers = nameSort match {
case NameSort.ASC => sqls"ORDER BY fqdn ASC, type ASC "
case NameSort.DESC => sqls"ORDER BY fqdn DESC, type ASC "
}
else {
sqls"ORDER BY fqdn DESC, type ASC "
val recordTypeSortQualifiers = recordTypeSort match {
case RecordTypeSort.ASC => sqls"ORDER BY type ASC"
case RecordTypeSort.DESC => sqls"ORDER BY type DESC"
case RecordTypeSort.NONE => nameSortQualifiers
}
val recordLimit = maxPlusOne match {
@@ -238,7 +245,7 @@ class MySqlRecordSetRepository extends RecordSetRepository with Monitored {
case None => sqls""
}
val finalQualifiers = qualifiers.append(recordLimit)
val finalQualifiers = recordTypeSortQualifiers.append(recordLimit)
// construct query
val initialQuery = sqls"SELECT data, fqdn FROM recordset "
@@ -278,7 +285,8 @@ class MySqlRecordSetRepository extends RecordSetRepository with Monitored {
maxItems = maxItems,
recordNameFilter = recordNameFilter,
recordTypeFilter = recordTypeFilter,
nameSort = nameSort
nameSort = nameSort,
recordTypeSort = recordTypeSort
)
}
}

View File

@@ -152,8 +152,11 @@
<thead>
<tr>
<th class="col-md-4">
<a class="force-cursor" ng-click="toggleNameSort()">FQDN
<i class="fa {{nameSortSymbol}}"></i>
<a class="fa-force-cursor" ng-click="toggleNameSort()">FQDN
<div class="fa-chevron-icon">
<i class="fa fa-chevron-circle-up {{nameSortSymbolUp}}"></i>
<i class="fa fa-chevron-circle-down {{nameSortSymbolDown}}"></i>
</div>
</a>
</th>
<th>Type</th>

View File

@@ -105,11 +105,21 @@
<thead>
<tr>
<th class="col-md-4">
<a class="force-cursor" ng-click="toggleNameSort()">Name
<i class="fa {{nameSortSymbol}}"></i>
<a class="fa-force-cursor" ng-click="toggleNameSort()">Name
<div class="fa-chevron-icon">
<i class="fa fa-chevron-circle-up {{nameSortSymbolUp}}"></i>
<i class="fa fa-chevron-circle-down {{nameSortSymbolDown}}"></i>
</div>
</a>
</th>
<th>
<a class="fa-force-cursor" ng-click="toggleRecordTypeSort()">Type
<div class="fa-chevron-icon">
<i class="fa fa-chevron-circle-up {{recordTypeSortSymbolUp}}"></i>
<i class="fa fa-chevron-circle-down {{recordTypeSortSymbolDown}}"></i>
</div>
</a>
</th>
<th>Type</th>
<th>TTL</th>
<th class="col-md-4">Record Data</th>
@if(meta.sharedDisplayEnabled) {

View File

@@ -142,6 +142,25 @@ a.action-link {
cursor: pointer;
}
.fa-force-cursor {
cursor: pointer;
display: inline-flex;
}
.fa-chevron-icon {
display: inline-flex;
flex-direction: column;
margin-left: 5px;
}
.toggle-on{
color: #1b1e24
}
.toggle-off{
color: #D3D3D3;
}
.vinyldns-login {
background: #1b1e24!important;
}

View File

@@ -23,7 +23,11 @@ angular.module('controller.records', [])
$scope.query = "";
$scope.nameSort = "asc";
$scope.nameSortSymbol = "fa-chevron-up";
$scope.recordTypeSort = "none";
$scope.nameSortSymbolUp = "toggle-on";
$scope.nameSortSymbolDown = "toggle-off";
$scope.recordTypeSortSymbolUp = "toggle-off";
$scope.recordTypeSortSymbolDown = "toggle-off";
$scope.alerts = [];
$scope.recordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
@@ -475,7 +479,7 @@ angular.module('controller.records', [])
updateRecordDisplay(response.data.recordSets);
}
return recordsService
.listRecordSetsByZone($scope.zoneId, recordsPaging.maxItems, undefined, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort)
.listRecordSetsByZone($scope.zoneId, recordsPaging.maxItems, undefined, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort, $scope.recordTypeSort)
.then(success)
.catch(function (error){
handleError(error, 'recordsService::listRecordSetsByZone-failure');
@@ -516,7 +520,7 @@ angular.module('controller.records', [])
$scope.prevPage = function() {
var startFrom = pagingService.getPrevStartFrom(recordsPaging);
return recordsService
.listRecordSetsByZone($scope.zoneId, recordsPaging.maxItems, startFrom, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort)
.listRecordSetsByZone($scope.zoneId, recordsPaging.maxItems, startFrom, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort, $scope.recordTypeSort)
.then(function(response) {
recordsPaging = pagingService.prevPageUpdate(response.data.nextId, recordsPaging);
updateRecordDisplay(response.data.recordSets);
@@ -528,7 +532,7 @@ angular.module('controller.records', [])
$scope.nextPage = function() {
return recordsService
.listRecordSetsByZone($scope.zoneId, recordsPaging.maxItems, recordsPaging.next, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort)
.listRecordSetsByZone($scope.zoneId, recordsPaging.maxItems, recordsPaging.next, $scope.query, $scope.selectedRecordTypes.toString(), $scope.nameSort, $scope.recordTypeSort)
.then(function(response) {
var recordSets = response.data.recordSets;
recordsPaging = pagingService.nextPageUpdate(recordSets, response.data.nextId, recordsPaging);
@@ -543,12 +547,33 @@ angular.module('controller.records', [])
};
$scope.toggleNameSort = function() {
$scope.recordTypeSort = "none"
$scope.recordTypeSortSymbolDown = "toggle-off";
$scope.recordTypeSortSymbolUp = "toggle-off";
if ($scope.nameSort == "asc") {
$scope.nameSort = "desc";
$scope.nameSortSymbol = "fa-chevron-down";
$scope.nameSortSymbolDown = "toggle-on";
$scope.nameSortSymbolUp = "toggle-off";
} else {
$scope.nameSort = "asc";
$scope.nameSortSymbol = "fa-chevron-up";
$scope.nameSortSymbolDown = "toggle-off";
$scope.nameSortSymbolUp = "toggle-on";
}
return $scope.refreshRecords();
};
$scope.toggleRecordTypeSort = function() {
$scope.nameSort = ""
$scope.nameSortSymbolDown = "toggle-off";
$scope.nameSortSymbolUp = "toggle-off";
if ($scope.recordTypeSort == "asc") {
$scope.recordTypeSort = "desc";
$scope.recordTypeSortSymbolDown = "toggle-on";
$scope.recordTypeSortSymbolUp = "toggle-off";
} else {
$scope.recordTypeSort = "asc";
$scope.recordTypeSortSymbolDown = "toggle-off";
$scope.recordTypeSortSymbolUp = "toggle-on";
}
return $scope.refreshRecords();
};

View File

@@ -275,13 +275,15 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100;
var expectedStartFrom = undefined;
var expectedQuery = this.scope.query;
var expectedSort = "asc";
var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.refreshRecords();
expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual(
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, "", expectedSort]);
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, "", expectedNameSort, expectedRecordTypeSort]);
});
it('next page should call listRecordSetsByZone with the correct parameters', function () {
@@ -302,13 +304,14 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100;
var expectedStartFrom = undefined;
var expectedQuery = this.scope.query;
var expectedSort = "asc";
var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.nextPage();
expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual(
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, "", expectedSort]);
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, "", expectedNameSort, expectedRecordTypeSort]);
});
it('prev page should call listRecordSetsByZone with the correct parameters', function () {
@@ -329,16 +332,17 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100;
var expectedStartFrom = undefined;
var expectedQuery = this.scope.query;
var expectedSort = "asc";
var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.prevPage();
expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual(
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, '', expectedSort]);
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, '', expectedNameSort, expectedRecordTypeSort]);
});
it('toggle sort should call listRecordSetsByZone with the correct parameters', function () {
it('toggle name sort should call listRecordSetsByZone with the correct parameters', function () {
var mockRecords = {data: { recordSets: [
{ name: "dummy",
records: [{address: "1.1.1.1"}],
@@ -357,13 +361,45 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100;
var expectedStartFrom = undefined;
var expectedQuery = this.scope.query;
var expectedSort = "desc";
var expectedNameSort = "desc";
var expectedRecordTypeSort = "none";
this.scope.toggleNameSort();
expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual(
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, '', expectedSort]);
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, '', expectedNameSort, expectedRecordTypeSort]);
});
it('toggle record type sort should call listRecordSetsByZone with the correct parameters', function () {
var mockRecords = {data: { recordSets: [
{ name: "dummy",
records: [{address: "1.1.1.1"}],
status: "Active",
ttl: 38400,
type: "A"}
],
maxItems: 100,
nameSort: "",
recordTypeSort: "asc"}};
var listRecordSetsByZone = spyOn(this.recordsService, 'listRecordSetsByZone')
.and.stub()
.and.returnValue(this.q.when(mockRecords));
var expectedZoneId = this.scope.zoneId;
var expectedMaxItems = 100;
var expectedStartFrom = undefined;
var expectedQuery = this.scope.query;
var expectedNameSort = "";
var expectedRecordTypeSort = "asc";
this.scope.toggleRecordTypeSort();
expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual(
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, '', expectedNameSort, expectedRecordTypeSort]);
});
it('filter by record type should call listRecordSetsByZone with the correct parameters', function () {
@@ -386,13 +422,15 @@ describe('Controller: RecordsController', function () {
var expectedStartFrom = undefined;
var expectedQuery = this.scope.query;
var expectedRecordTypeFilter = "A";
var expectedSort = "asc";
var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.toggleCheckedRecordType("A");
this.scope.refreshRecords();
expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual(
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, expectedRecordTypeFilter, expectedSort]);
[expectedZoneId, expectedMaxItems, expectedStartFrom, expectedQuery, expectedRecordTypeFilter, expectedNameSort, expectedRecordTypeSort]);
});
});

View File

@@ -24,7 +24,8 @@
$scope.recordSetChanges = {};
$scope.alerts = [];
$scope.nameSort = "asc";
$scope.nameSortSymbol = "fa-chevron-up";
$scope.nameSortSymbolUp = "toggle-on";
$scope.nameSortSymbolDown = "toggle-off";
$scope.readRecordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', "SOA", 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
$scope.selectedRecordTypes = [];
$scope.groups = [];
@@ -105,10 +106,12 @@
$scope.toggleNameSort = function() {
if ($scope.nameSort == "asc") {
$scope.nameSort = "desc";
$scope.nameSortSymbol = "fa-chevron-down";
$scope.nameSortSymbolDown = "toggle-on";
$scope.nameSortSymbolUp = "toggle-off";
} else {
$scope.nameSort = "asc";
$scope.nameSortSymbol = "fa-chevron-up";
$scope.nameSortSymbolDown = "toggle-off";
$scope.nameSortSymbolUp = "toggle-on";
}
return $scope.refreshRecords();
};

View File

@@ -55,7 +55,7 @@ angular.module('service.records', [])
return promis
};
this.listRecordSetsByZone = function (id, limit, startFrom, nameFilter, typeFilter, nameSort) {
this.listRecordSetsByZone = function (id, limit, startFrom, nameFilter, typeFilter, nameSort, recordTypeSort) {
if (nameFilter == "") {
nameFilter = null;
}
@@ -65,12 +65,16 @@ angular.module('service.records', [])
if (nameSort == "") {
nameSort = null;
}
if (recordTypeSort == "") {
recordTypeSort = null;
}
var params = {
"maxItems": limit,
"startFrom": startFrom,
"recordNameFilter": nameFilter,
"recordTypeFilter": typeFilter,
"nameSort": nameSort
"nameSort": nameSort,
"recordTypeSort": recordTypeSort
};
var url = utilityService.urlBuilder("/api/zones/"+id+"/recordsets", params);
return $http.get(url);