2
0
mirror of https://github.com/VinylDNS/vinyldns synced 2025-09-04 00:05:12 +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.api.{MySqlApiIntegrationSpec, ResultHelpers}
import vinyldns.core.TestRecordSetData._ import vinyldns.core.TestRecordSetData._
import vinyldns.core.domain.backend.{Backend, BackendResolver} 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.domain.zone.{Zone, ZoneChange, ZoneChangeType}
import vinyldns.core.health.HealthCheck.HealthCheck import vinyldns.core.health.HealthCheck.HealthCheck
import vinyldns.route53.backend.{Route53Backend, Route53BackendConfig} 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 // We should have both the record we created above as well as at least one NS record
val results = recordSetRepository 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() .unsafeRunSync()
results.recordSets.map(_.typ).distinct should contain theSameElementsAs List( results.recordSets.map(_.typ).distinct should contain theSameElementsAs List(
rsOk.typ, rsOk.typ,

View File

@@ -19,7 +19,7 @@ package vinyldns.api.domain.record
import cats.effect.IO import cats.effect.IO
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import scalikejdbc.DB 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 import vinyldns.mysql.TransactionProvider
@@ -30,7 +30,7 @@ class RecordSetCacheService(recordSetRepository: RecordSetRepository,
final def populateRecordSetCache(nextId: Option[String] = None): IO[ListRecordSetResults] = { final def populateRecordSetCache(nextId: Option[String] = None): IO[ListRecordSetResults] = {
logger.info(s"Populating recordset data. Starting at $nextId") logger.info(s"Populating recordset data. Starting at $nextId")
for { 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 => _ <- executeWithinTransaction { db: DB =>
IO { 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.record.RecordType.RecordType
import vinyldns.core.domain.DomainHelpers.ensureTrailingDot import vinyldns.core.domain.DomainHelpers.ensureTrailingDot
import vinyldns.core.domain.backend.{Backend, BackendResolver} import vinyldns.core.domain.backend.{Backend, BackendResolver}
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import scala.util.matching.Regex import scala.util.matching.Regex
@@ -408,7 +409,8 @@ class RecordSetService(
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort, nameSort: NameSort,
authPrincipal: AuthPrincipal authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] = ): Result[ListGlobalRecordSetsResponse] =
for { for {
_ <- validRecordNameFilterLength(recordNameFilter).toResult _ <- validRecordNameFilterLength(recordNameFilter).toResult
@@ -421,7 +423,8 @@ class RecordSetService(
Some(formattedRecordNameFilter), Some(formattedRecordNameFilter),
recordTypeFilter, recordTypeFilter,
recordOwnerGroupFilter, recordOwnerGroupFilter,
nameSort nameSort,
recordTypeSort
) )
.toResult[ListRecordSetResults] .toResult[ListRecordSetResults]
rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet
@@ -459,7 +462,8 @@ class RecordSetService(
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort, nameSort: NameSort,
authPrincipal: AuthPrincipal authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] = { ): Result[ListGlobalRecordSetsResponse] = {
for { for {
_ <- validRecordNameFilterLength(recordNameFilter).toResult _ <- validRecordNameFilterLength(recordNameFilter).toResult
@@ -484,7 +488,8 @@ class RecordSetService(
Some(formattedRecordNameFilter), Some(formattedRecordNameFilter),
recordTypeFilter, recordTypeFilter,
recordOwnerGroupFilter, recordOwnerGroupFilter,
nameSort nameSort,
recordTypeSort
).toResult[ListRecordSetResults] ).toResult[ListRecordSetResults]
} }
rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet
@@ -512,7 +517,8 @@ class RecordSetService(
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort, nameSort: NameSort,
authPrincipal: AuthPrincipal authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListRecordSetsByZoneResponse] = ): Result[ListRecordSetsByZoneResponse] =
for { for {
zone <- getZone(zoneId) zone <- getZone(zoneId)
@@ -525,7 +531,8 @@ class RecordSetService(
recordNameFilter, recordNameFilter,
recordTypeFilter, recordTypeFilter,
recordOwnerGroupFilter, recordOwnerGroupFilter,
nameSort nameSort,
recordTypeSort
) )
.toResult[ListRecordSetResults] .toResult[ListRecordSetResults]
rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet rsOwnerGroupIds = recordSetResults.recordSets.flatMap(_.ownerGroupId).toSet
@@ -540,7 +547,8 @@ class RecordSetService(
recordSetResults.recordNameFilter, recordSetResults.recordNameFilter,
recordSetResults.recordTypeFilter, recordSetResults.recordTypeFilter,
recordSetResults.recordOwnerGroupFilter, recordSetResults.recordOwnerGroupFilter,
recordSetResults.nameSort recordSetResults.nameSort,
recordSetResults.recordTypeSort
) )
def getRecordSetChange( def getRecordSetChange(

View File

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

View File

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

View File

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

View File

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

View File

@@ -1679,7 +1679,8 @@ class RecordSetServiceSpec
List(sharedZoneRecord), List(sharedZoneRecord),
recordNameFilter = Some("aaaa*"), recordNameFilter = Some("aaaa*"),
nameSort = NameSort.ASC, nameSort = NameSort.ASC,
recordOwnerGroupFilter = Some("owner group id") recordOwnerGroupFilter = Some("owner group id") ,
recordTypeSort = RecordTypeSort.NONE
) )
) )
).when(mockRecordRepo) ).when(mockRecordRepo)
@@ -1690,7 +1691,8 @@ class RecordSetServiceSpec
recordNameFilter = any[Option[String]], recordNameFilter = any[Option[String]],
recordTypeFilter = any[Option[Set[RecordType.RecordType]]], recordTypeFilter = any[Option[Set[RecordType.RecordType]]],
recordOwnerGroupFilter = any[Option[String]], recordOwnerGroupFilter = any[Option[String]],
nameSort = any[NameSort.NameSort] nameSort = any[NameSort.NameSort],
recordTypeSort = any[RecordTypeSort.RecordTypeSort]
) )
val result: ListGlobalRecordSetsResponse = val result: ListGlobalRecordSetsResponse =
@@ -1702,7 +1704,8 @@ class RecordSetServiceSpec
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"), recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC, nameSort = NameSort.ASC,
authPrincipal = sharedAuth authPrincipal = sharedAuth,
recordTypeSort = RecordTypeSort.ASC
) )
.value.unsafeRunSync().toOption.get .value.unsafeRunSync().toOption.get
@@ -1727,7 +1730,8 @@ class RecordSetServiceSpec
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"), recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC, nameSort = NameSort.ASC,
authPrincipal = okAuth authPrincipal = okAuth,
recordTypeSort = RecordTypeSort.ASC
) )
.value.unsafeRunSync().swap.toOption.get .value.unsafeRunSync().swap.toOption.get
@@ -1751,7 +1755,8 @@ class RecordSetServiceSpec
List(sharedZoneRecord), List(sharedZoneRecord),
recordNameFilter = Some("aaaa*"), recordNameFilter = Some("aaaa*"),
nameSort = NameSort.ASC, nameSort = NameSort.ASC,
recordOwnerGroupFilter = Some("owner group id") recordOwnerGroupFilter = Some("owner group id"),
recordTypeSort = RecordTypeSort.NONE
) )
) )
).when(mockRecordDataRepo) ).when(mockRecordDataRepo)
@@ -1774,7 +1779,8 @@ class RecordSetServiceSpec
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"), recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC, nameSort = NameSort.ASC,
authPrincipal = sharedAuth authPrincipal = sharedAuth,
recordTypeSort = RecordTypeSort.ASC
) )
.value.unsafeRunSync().toOption.get .value.unsafeRunSync().toOption.get
@@ -1799,7 +1805,8 @@ class RecordSetServiceSpec
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = Some("owner group id"), recordOwnerGroupFilter = Some("owner group id"),
nameSort = NameSort.ASC, nameSort = NameSort.ASC,
authPrincipal = okAuth authPrincipal = okAuth,
recordTypeSort = RecordTypeSort.ASC
) )
.value.unsafeRunSync().swap.toOption.get .value.unsafeRunSync().swap.toOption.get
@@ -1818,7 +1825,8 @@ class RecordSetServiceSpec
IO.pure( IO.pure(
ListRecordSetResults( ListRecordSetResults(
List(sharedZoneRecord, sharedZoneRecordNotFoundOwnerGroup), List(sharedZoneRecord, sharedZoneRecordNotFoundOwnerGroup),
nameSort = NameSort.ASC nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
) )
) )
).when(mockRecordRepo) ).when(mockRecordRepo)
@@ -1829,7 +1837,8 @@ class RecordSetServiceSpec
recordNameFilter = None, recordNameFilter = None,
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = None, recordOwnerGroupFilter = None,
nameSort = NameSort.ASC nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
) )
val result: ListRecordSetsByZoneResponse = val result: ListRecordSetsByZoneResponse =
@@ -1842,7 +1851,8 @@ class RecordSetServiceSpec
authPrincipal = sharedAuth, authPrincipal = sharedAuth,
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = None, recordOwnerGroupFilter = None,
nameSort = NameSort.ASC nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
) )
.value.unsafeRunSync().toOption.get .value.unsafeRunSync().toOption.get
@@ -1863,7 +1873,7 @@ class RecordSetServiceSpec
.when(mockGroupRepo) .when(mockGroupRepo)
.getGroups(Set()) .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) .when(mockRecordRepo)
.listRecordSets( .listRecordSets(
zoneId = Some(okZone.id), zoneId = Some(okZone.id),
@@ -1872,7 +1882,8 @@ class RecordSetServiceSpec
recordNameFilter = None, recordNameFilter = None,
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = None, recordOwnerGroupFilter = None,
nameSort = NameSort.ASC nameSort = NameSort.ASC,
recordTypeSort = RecordTypeSort.ASC
) )
val result: ListRecordSetsByZoneResponse = val result: ListRecordSetsByZoneResponse =
@@ -1885,7 +1896,8 @@ class RecordSetServiceSpec
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = None, recordOwnerGroupFilter = None,
nameSort = NameSort.ASC, 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 .value.unsafeRunSync().toOption.get
@@ -1904,7 +1916,8 @@ class RecordSetServiceSpec
recordTypeFilter = None, recordTypeFilter = None,
recordOwnerGroupFilter = None, recordOwnerGroupFilter = None,
nameSort = NameSort.ASC, nameSort = NameSort.ASC,
authPrincipal = okAuth authPrincipal = okAuth,
recordTypeSort = RecordTypeSort.ASC
) )
.value.unsafeRunSync().swap.toOption.get .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.backend.{Backend, BackendResolver}
import vinyldns.core.domain.record.NameSort.NameSort import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.domain.zone.{Zone, ZoneConnection, ZoneStatus} import vinyldns.core.domain.zone.{Zone, ZoneConnection, ZoneStatus}
class ZoneViewLoaderSpec extends AnyWordSpec with Matchers with MockitoSugar with DnsConversions { 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] 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) .when(mockRecordSetRepo)
.listRecordSets( .listRecordSets(
any[Option[String]], any[Option[String]],
@@ -104,7 +105,8 @@ class ZoneViewLoaderSpec extends AnyWordSpec with Matchers with MockitoSugar wit
any[Option[String]], any[Option[String]],
any[Option[Set[RecordType]]], any[Option[Set[RecordType]]],
any[Option[String]], any[Option[String]],
any[NameSort] any[NameSort],
any[RecordTypeSort]
) )
val underTest = VinylDNSZoneViewLoader(testZone, mockRecordSetRepo, mockRecordSetDataRepo) val underTest = VinylDNSZoneViewLoader(testZone, mockRecordSetRepo, mockRecordSetDataRepo)

View File

@@ -40,6 +40,7 @@ import vinyldns.core.domain.zone._
import cats.syntax.all._ import cats.syntax.all._
import org.slf4j.{Logger, LoggerFactory} import org.slf4j.{Logger, LoggerFactory}
import vinyldns.api.engine.ZoneSyncHandler.{monitor, time} import vinyldns.api.engine.ZoneSyncHandler.{monitor, time}
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.mysql.TransactionProvider import vinyldns.mysql.TransactionProvider
class ZoneSyncHandlerSpec class ZoneSyncHandlerSpec
@@ -311,7 +312,7 @@ class ZoneSyncHandlerSpec
) )
doReturn( 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) ).when(recordSetRepo)
.listRecordSets( .listRecordSets(
any[Option[String]], any[Option[String]],
@@ -320,7 +321,8 @@ class ZoneSyncHandlerSpec
any[Option[String]], any[Option[String]],
any[Option[Set[RecordType]]], any[Option[Set[RecordType]]],
any[Option[String]], any[Option[String]],
any[NameSort] any[NameSort],
any[RecordTypeSort],
) )
doReturn(IO(testChangeSet)).when(recordSetRepo).apply(any[DB], any[ChangeSet]) 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.auth.AuthPrincipal
import vinyldns.core.domain.record.RecordType.RecordType import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record._ 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 cats.effect._
import scalikejdbc._ import scalikejdbc._
import vinyldns.core.domain.membership._ import vinyldns.core.domain.membership._
import vinyldns.core.domain.record.NameSort.NameSort import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.domain.zone.ZoneRepository.DuplicateZoneError import vinyldns.core.domain.zone.ZoneRepository.DuplicateZoneError
// Empty implementations let our other test classes just edit with the methods they need // 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], recordNameFilter: Option[String],
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort nameSort: NameSort,
recordTypeSort: RecordTypeSort
): IO[ListRecordSetResults] = ): 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]] = def getRecordSets(zoneId: String, name: String, typ: RecordType): IO[List[RecordSet]] =
@@ -73,7 +75,7 @@ trait EmptyRecordSetCacheRepo extends RecordSetCacheRepository {
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort nameSort: NameSort
): IO[ListRecordSetResults] = ): IO[ListRecordSetResults] =
IO.pure(ListRecordSetResults(nameSort = nameSort)) IO.pure(ListRecordSetResults(nameSort = nameSort, recordTypeSort = RecordTypeSort.NONE))
} }
trait EmptyZoneRepo extends ZoneRepository { 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.NameSort.NameSort
import vinyldns.core.domain.record.RecordSetChangeType.RecordSetChangeType import vinyldns.core.domain.record.RecordSetChangeType.RecordSetChangeType
import vinyldns.core.domain.record.RecordType._ import vinyldns.core.domain.record.RecordType._
import vinyldns.core.domain.record.RecordTypeSort.{ASC, DESC, RecordTypeSort}
import vinyldns.core.domain.record._ import vinyldns.core.domain.record._
import vinyldns.core.domain.zone._ import vinyldns.core.domain.zone._
@@ -529,7 +530,8 @@ class RecordSetRoutingSpec
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort, nameSort: NameSort,
authPrincipal: AuthPrincipal authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] = { ): Result[ListGlobalRecordSetsResponse] = {
if (recordTypeFilter.contains(Set(CNAME))) { if (recordTypeFilter.contains(Set(CNAME))) {
Right( Right(
@@ -587,7 +589,8 @@ class RecordSetRoutingSpec
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort, nameSort: NameSort,
authPrincipal: AuthPrincipal authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListGlobalRecordSetsResponse] = { ): Result[ListGlobalRecordSetsResponse] = {
if (recordTypeFilter.contains(Set(CNAME))) { if (recordTypeFilter.contains(Set(CNAME))) {
Right( Right(
@@ -637,10 +640,49 @@ class RecordSetRoutingSpec
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort, nameSort: NameSort,
authPrincipal: AuthPrincipal authPrincipal: AuthPrincipal,
recordTypeSort: RecordTypeSort
): Result[ListRecordSetsByZoneResponse] = { ): Result[ListRecordSetsByZoneResponse] = {
zoneId match { zoneId match {
case zoneNotFound.id => Left(ZoneNotFoundError(s"$zoneId")) 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)) => case okZone.id if recordTypeFilter.contains(Set(CNAME)) =>
Right( Right(
ListRecordSetsByZoneResponse( ListRecordSetsByZoneResponse(
@@ -653,7 +695,8 @@ class RecordSetRoutingSpec
recordNameFilter, recordNameFilter,
recordTypeFilter, recordTypeFilter,
recordOwnerGroupFilter, recordOwnerGroupFilter,
nameSort nameSort,
recordTypeSort=RecordTypeSort.ASC
) )
) )
case okZone.id if recordTypeFilter.isEmpty => case okZone.id if recordTypeFilter.isEmpty =>
@@ -670,7 +713,8 @@ class RecordSetRoutingSpec
recordNameFilter, recordNameFilter,
recordTypeFilter, recordTypeFilter,
None, 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 { "return recordsets of a specific type" in {
Get(s"/zones/${okZone.id}/recordsets?recordTypeFilter=cname") ~> recordSetRoute ~> check { Get(s"/zones/${okZone.id}/recordsets?recordTypeFilter=cname") ~> recordSetRoute ~> check {
status shouldBe StatusCodes.OK 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.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
object NameSort extends Enumeration { object NameSort extends Enumeration {
type NameSort = Value type NameSort = Value
@@ -26,6 +27,19 @@ object NameSort extends Enumeration {
def find(value: String): Value = value.toUpperCase match { def find(value: String): Value = value.toUpperCase match {
case "DESC" => NameSort.DESC case "DESC" => NameSort.DESC
case _ => NameSort.ASC 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, recordNameFilter: Option[String] = None,
recordTypeFilter: Option[Set[RecordType]] = None, recordTypeFilter: Option[Set[RecordType]] = None,
recordOwnerGroupFilter: Option[String] = None, recordOwnerGroupFilter: Option[String] = None,
nameSort: NameSort nameSort: NameSort,
recordTypeSort: RecordTypeSort
) )

View File

@@ -20,6 +20,7 @@ import cats.effect._
import scalikejdbc.DB import scalikejdbc.DB
import vinyldns.core.domain.record.NameSort.NameSort import vinyldns.core.domain.record.NameSort.NameSort
import vinyldns.core.domain.record.RecordType.RecordType import vinyldns.core.domain.record.RecordType.RecordType
import vinyldns.core.domain.record.RecordTypeSort.RecordTypeSort
import vinyldns.core.repository.Repository import vinyldns.core.repository.Repository
trait RecordSetRepository extends Repository { trait RecordSetRepository extends Repository {
@@ -33,7 +34,8 @@ trait RecordSetRepository extends Repository {
recordNameFilter: Option[String], recordNameFilter: Option[String],
recordTypeFilter: Option[Set[RecordType]], recordTypeFilter: Option[Set[RecordType]],
recordOwnerGroupFilter: Option[String], recordOwnerGroupFilter: Option[String],
nameSort: NameSort nameSort: NameSort,
recordTypeSort: RecordTypeSort
): IO[ListRecordSetResults] ): IO[ListRecordSetResults]
def getRecordSets(zoneId: String, name: String, typ: RecordType): IO[List[RecordSet]] 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 { "return all record sets in a zone when optional params are not set" in {
val existing = insert(okZone, 10).map(_.recordSet) val existing = insert(okZone, 10).map(_.recordSet)
val found = repo 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() .unsafeRunSync()
found.recordSets should contain theSameElementsAs existing.map( found.recordSets should contain theSameElementsAs existing.map(
r => recordSetWithFQDN(r, okZone) r => recordSetWithFQDN(r, okZone)
@@ -430,7 +430,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
val existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name) val existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name)
val startFrom = Some(PagingKey.toNextId(existing(2), true)) val startFrom = Some(PagingKey.toNextId(existing(2), true))
val found = repo 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() .unsafeRunSync()
(found.recordSets should contain).theSameElementsInOrderAs( (found.recordSets should contain).theSameElementsInOrderAs(
@@ -444,7 +444,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
val existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name) val existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name)
val startFrom = Some(PagingKey.toNextId(existing(1), true)) val startFrom = Some(PagingKey.toNextId(existing(1), true))
val found = repo 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() .unsafeRunSync()
(found.recordSets should contain).theSameElementsInOrderAs( (found.recordSets should contain).theSameElementsInOrderAs(
@@ -475,7 +475,8 @@ class MySqlRecordSetRepositoryIntegrationSpec
Some("*z*"), Some("*z*"),
None, None,
None, None,
NameSort.ASC NameSort.ASC,
RecordTypeSort.NONE
) )
.unsafeRunSync() .unsafeRunSync()
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames) (found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
@@ -493,7 +494,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
insert(changes) insert(changes)
val found = repo 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() .unsafeRunSync()
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames) (found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
} }
@@ -510,7 +511,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
insert(changes) insert(changes)
val found = repo 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() .unsafeRunSync()
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames) (found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
} }
@@ -528,14 +529,14 @@ class MySqlRecordSetRepositoryIntegrationSpec
insert(changes) insert(changes)
val found = repo 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() .unsafeRunSync()
(found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames) (found.recordSets.map(_.name) should contain).theSameElementsInOrderAs(expectedNames)
} }
"return select types of recordsets in a zone" in { "return select types of recordsets in a zone" in {
insert(okZone, 10).map(_.recordSet) insert(okZone, 10).map(_.recordSet)
val found = repo 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() .unsafeRunSync()
found.recordSets shouldBe List() found.recordSets shouldBe List()
found.recordTypeFilter shouldBe Some(Set(CNAME)) found.recordTypeFilter shouldBe Some(Set(CNAME))
@@ -543,18 +544,30 @@ class MySqlRecordSetRepositoryIntegrationSpec
"return all recordsets in a zone in descending order" in { "return all recordsets in a zone in descending order" in {
val existing = insert(okZone, 10).map(_.recordSet) val existing = insert(okZone, 10).map(_.recordSet)
val found = repo 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() .unsafeRunSync()
found.recordSets should contain theSameElementsAs existing.map( found.recordSets should contain theSameElementsAs existing.map(
r => recordSetWithFQDN(r, okZone) r => recordSetWithFQDN(r, okZone)
) )
found.nameSort shouldBe NameSort.DESC 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 { "pages through the list properly" in {
// load 5 records, pages of 2, last page should have 1 result and no next id // 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 existing = insert(okZone, 5).map(_.recordSet).sortBy(_.name)
val page1 = repo 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() .unsafeRunSync()
(page1.recordSets should contain).theSameElementsInOrderAs( (page1.recordSets should contain).theSameElementsInOrderAs(
existing existing
@@ -564,7 +577,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
page1.nextId shouldBe Some(PagingKey.toNextId(page1.recordSets(1), true)) page1.nextId shouldBe Some(PagingKey.toNextId(page1.recordSets(1), true))
val page2 = repo 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() .unsafeRunSync()
(page2.recordSets should contain).theSameElementsInOrderAs( (page2.recordSets should contain).theSameElementsInOrderAs(
existing existing
@@ -574,7 +587,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
page2.nextId shouldBe Some(PagingKey.toNextId(page2.recordSets(1), true)) page2.nextId shouldBe Some(PagingKey.toNextId(page2.recordSets(1), true))
val page3 = repo 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() .unsafeRunSync()
(page3.recordSets should contain).theSameElementsInOrderAs( (page3.recordSets should contain).theSameElementsInOrderAs(
existing existing
@@ -597,7 +610,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
val existing = editedChanges.map(_.recordSet) val existing = editedChanges.map(_.recordSet)
val page1 = repo 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() .unsafeRunSync()
(page1.recordSets should contain).theSameElementsInOrderAs( (page1.recordSets should contain).theSameElementsInOrderAs(
List(recordSetWithFQDN(existing.head, okZone), recordSetWithFQDN(existing(1), okZone)) 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)) page1.nextId shouldBe Some(PagingKey.toNextId(page1.recordSets.last, true))
val page2 = repo 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() .unsafeRunSync()
(page2.recordSets should contain).theSameElementsInOrderAs( (page2.recordSets should contain).theSameElementsInOrderAs(
List(recordSetWithFQDN(existing(2), okZone), recordSetWithFQDN(existing(3), okZone)) 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)) page2.nextId shouldBe Some(PagingKey.toNextId(page2.recordSets.last, true))
val page3 = repo 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() .unsafeRunSync()
(page3.recordSets should contain) (page3.recordSets should contain)
.theSameElementsInOrderAs(List(recordSetWithFQDN(existing(4), okZone))) .theSameElementsInOrderAs(List(recordSetWithFQDN(existing(4), okZone)))
page3.nextId shouldBe None 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 existing = insert(okZone, 10).map(_.recordSet)
val found = repo 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() .unsafeRunSync()
found.recordSets should contain theSameElementsAs existing.map( found.recordSets should contain theSameElementsAs existing.map(
r => recordSetWithFQDN(r, okZone) 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 { "return applicable recordsets in descending order when recordNameFilter is given and name sort is descending" in {
val existing = insert(okZone, 10).map(_.recordSet) val existing = insert(okZone, 10).map(_.recordSet)
val found = repo 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() .unsafeRunSync()
found.recordSets should contain theSameElementsAs existing found.recordSets should contain theSameElementsAs existing
.map(r => recordSetWithFQDN(r, okZone)) .map(r => recordSetWithFQDN(r, okZone))
@@ -639,7 +652,7 @@ class MySqlRecordSetRepositoryIntegrationSpec
} }
"return no recordsets when no zoneId or recordNameFilter are given" in { "return no recordsets when no zoneId or recordNameFilter are given" in {
val found = 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 found.recordSets shouldBe empty
} }
} }

View File

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

View File

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

View File

@@ -152,8 +152,11 @@
<thead> <thead>
<tr> <tr>
<th class="col-md-4"> <th class="col-md-4">
<a class="force-cursor" ng-click="toggleNameSort()">FQDN <a class="fa-force-cursor" ng-click="toggleNameSort()">FQDN
<i class="fa {{nameSortSymbol}}"></i> <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> </a>
</th> </th>
<th>Type</th> <th>Type</th>

View File

@@ -105,11 +105,21 @@
<thead> <thead>
<tr> <tr>
<th class="col-md-4"> <th class="col-md-4">
<a class="force-cursor" ng-click="toggleNameSort()">Name <a class="fa-force-cursor" ng-click="toggleNameSort()">Name
<i class="fa {{nameSortSymbol}}"></i> <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> </a>
</th> </th>
<th>Type</th>
<th>TTL</th> <th>TTL</th>
<th class="col-md-4">Record Data</th> <th class="col-md-4">Record Data</th>
@if(meta.sharedDisplayEnabled) { @if(meta.sharedDisplayEnabled) {

View File

@@ -142,6 +142,25 @@ a.action-link {
cursor: pointer; 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 { .vinyldns-login {
background: #1b1e24!important; background: #1b1e24!important;
} }

View File

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

View File

@@ -275,13 +275,15 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100; var expectedMaxItems = 100;
var expectedStartFrom = undefined; var expectedStartFrom = undefined;
var expectedQuery = this.scope.query; var expectedQuery = this.scope.query;
var expectedSort = "asc"; var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.refreshRecords(); this.scope.refreshRecords();
expect(listRecordSetsByZone.calls.count()).toBe(1); expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual( 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 () { it('next page should call listRecordSetsByZone with the correct parameters', function () {
@@ -302,13 +304,14 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100; var expectedMaxItems = 100;
var expectedStartFrom = undefined; var expectedStartFrom = undefined;
var expectedQuery = this.scope.query; var expectedQuery = this.scope.query;
var expectedSort = "asc"; var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.nextPage(); this.scope.nextPage();
expect(listRecordSetsByZone.calls.count()).toBe(1); expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual( 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 () { it('prev page should call listRecordSetsByZone with the correct parameters', function () {
@@ -329,16 +332,17 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100; var expectedMaxItems = 100;
var expectedStartFrom = undefined; var expectedStartFrom = undefined;
var expectedQuery = this.scope.query; var expectedQuery = this.scope.query;
var expectedSort = "asc"; var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.prevPage(); this.scope.prevPage();
expect(listRecordSetsByZone.calls.count()).toBe(1); expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual( 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: [ var mockRecords = {data: { recordSets: [
{ name: "dummy", { name: "dummy",
records: [{address: "1.1.1.1"}], records: [{address: "1.1.1.1"}],
@@ -357,13 +361,45 @@ describe('Controller: RecordsController', function () {
var expectedMaxItems = 100; var expectedMaxItems = 100;
var expectedStartFrom = undefined; var expectedStartFrom = undefined;
var expectedQuery = this.scope.query; var expectedQuery = this.scope.query;
var expectedSort = "desc"; var expectedNameSort = "desc";
var expectedRecordTypeSort = "none";
this.scope.toggleNameSort(); this.scope.toggleNameSort();
expect(listRecordSetsByZone.calls.count()).toBe(1); expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual( 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 () { 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 expectedStartFrom = undefined;
var expectedQuery = this.scope.query; var expectedQuery = this.scope.query;
var expectedRecordTypeFilter = "A"; var expectedRecordTypeFilter = "A";
var expectedSort = "asc"; var expectedNameSort = "asc";
var expectedRecordTypeSort = "none";
this.scope.toggleCheckedRecordType("A"); this.scope.toggleCheckedRecordType("A");
this.scope.refreshRecords(); this.scope.refreshRecords();
expect(listRecordSetsByZone.calls.count()).toBe(1); expect(listRecordSetsByZone.calls.count()).toBe(1);
expect(listRecordSetsByZone.calls.mostRecent().args).toEqual( 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.recordSetChanges = {};
$scope.alerts = []; $scope.alerts = [];
$scope.nameSort = "asc"; $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.readRecordTypes = ['A', 'AAAA', 'CNAME', 'DS', 'MX', 'NS', 'PTR', "SOA", 'SRV', 'NAPTR', 'SSHFP', 'TXT'];
$scope.selectedRecordTypes = []; $scope.selectedRecordTypes = [];
$scope.groups = []; $scope.groups = [];
@@ -105,10 +106,12 @@
$scope.toggleNameSort = function() { $scope.toggleNameSort = function() {
if ($scope.nameSort == "asc") { if ($scope.nameSort == "asc") {
$scope.nameSort = "desc"; $scope.nameSort = "desc";
$scope.nameSortSymbol = "fa-chevron-down"; $scope.nameSortSymbolDown = "toggle-on";
$scope.nameSortSymbolUp = "toggle-off";
} else { } else {
$scope.nameSort = "asc"; $scope.nameSort = "asc";
$scope.nameSortSymbol = "fa-chevron-up"; $scope.nameSortSymbolDown = "toggle-off";
$scope.nameSortSymbolUp = "toggle-on";
} }
return $scope.refreshRecords(); return $scope.refreshRecords();
}; };

View File

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