2
0
mirror of https://github.com/VinylDNS/vinyldns synced 2025-08-22 02:02:14 +00:00

DNS Change detail notices (#901)

This commit is contained in:
Britney Wright 2019-11-11 16:09:57 -05:00 committed by GitHub
parent ea7c77951c
commit e3da818101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 367 additions and 7 deletions

View File

@ -18,7 +18,7 @@ package controllers
import actions.SecuritySupport
import javax.inject.{Inject, Singleton}
import models.{CustomLinks, Meta}
import models.{CustomLinks, DnsChangeNotices, Meta}
import org.slf4j.LoggerFactory
import play.api.Configuration
import play.api.mvc._
@ -87,10 +87,11 @@ class FrontendController @Inject() (
def viewBatchChange(batchId: String): Action[AnyContent] = userAction.async { implicit request =>
logger.info(s"View Batch Change for $batchId")
val canReview = request.user.isSuper || request.user.isSupport
val dnsChangeNotices = configuration.get[DnsChangeNotices]("dns-change-notices")
Future(
Ok(
views.html.dnsChanges
.dnsChangeDetail(request.user.userName, canReview)
.dnsChangeDetail(request.user.userName, canReview, dnsChangeNotices)
)
)
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2018 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package models
import com.typesafe.config.Config
import models.DnsChangeNoticeType.DnsChangeNoticeType
import models.DnsChangeStatus.DnsChangeStatus
import play.api.libs.json.{JsValue, Json, Writes}
import play.api.ConfigLoader
import net.ceedubs.ficus.Ficus._
import net.ceedubs.ficus.readers.EnumerationReader._
import scala.collection.JavaConverters._
case class DnsChangeNotices(notices: JsValue)
object DnsChangeNotices {
implicit val dnsChangeNoticeWrites: Writes[DnsChangeNotice] = Json.writes[DnsChangeNotice]
implicit val configLoader: ConfigLoader[DnsChangeNotices] =
new ConfigLoader[DnsChangeNotices] {
def load(config: Config, path: String): DnsChangeNotices = {
val notices = config
.getConfigList(path)
.asScala
.map(formatDnsChangeNotice)
DnsChangeNotices(Json.toJson(notices))
}
}
def formatDnsChangeNotice(config: Config): DnsChangeNotice = {
val status = config.as[DnsChangeStatus]("status")
val alertType = config.as[DnsChangeNoticeType]("alertType")
val text = config.getString("text")
val hrefText = config.getOrElse[String]("hrefText", "")
val href = config.getOrElse[String]("href", "")
DnsChangeNotice(status, alertType, text, hrefText, href)
}
}
case class DnsChangeNotice(
status: DnsChangeStatus,
alertType: DnsChangeNoticeType,
text: String,
hrefText: String,
href: String
)
object DnsChangeStatus extends Enumeration {
type DnsChangeStatus = Value
val Cancelled, Complete, Failed, PartialFailure, PendingProcessing, PendingReview, Rejected, Scheduled = Value
}
object DnsChangeNoticeType extends Enumeration {
type DnsChangeNoticeType = Value
val info, success, warning, danger = Value
}

View File

@ -1,8 +1,8 @@
@(rootAccountName: String, rootAccountCanReview: Boolean)(implicit request: play.api.mvc.Request[Any], customLinks: models.CustomLinks, meta: models.Meta)
@(rootAccountName: String, rootAccountCanReview: Boolean, dnsChangeNotices: DnsChangeNotices)(implicit request: play.api.mvc.Request[Any], customLinks: models.CustomLinks, meta: models.Meta)
@content = {
<!-- PAGE CONTENT -->
<div class="right_col" role="main">
<div class="right_col" role="main" ng-init="notices = @dnsChangeNotices.notices;">
<!-- BREADCRUMB -->
<ul class="breadcrumb">
@ -36,6 +36,9 @@
<notification ng-model="alert"></notification>
</div>
</div>
<notice ng-model="notice" ng-if="notice"></notice>
<div class="row">
<div class="col-md-12">
<p><strong>ID:</strong> {{batch.id}}</p>

View File

@ -169,6 +169,7 @@
<script src="/public/lib/directives/directives.modals.modal.js"></script>
<script src="/public/lib/directives/directives.modals.record.js"></script>
<script src="/public/lib/directives/directives.modals.zoneconnection.js"></script>
<script src="/public/lib/directives/directives.notices.js"></script>
<script src="/public/lib/directives/directives.notifications.js"></script>
<script src="/public/lib/directives/directives.validations.js"></script>
<script src="/public/lib/directives/directives.validations.zones.js"></script>

View File

@ -151,6 +151,26 @@ links = [
}
]
dns-change-notices = [
{
status = "Complete"
alertType = "info"
text = "Your changes are fully implemented. Keep in mind it may take a few hours for the changes to fully propogate out."
},
{
status = "PendingReview"
alertType = "info"
text = "Your DNS Change requires further review. It will be approved and processed following the review."
hrefText = "See the docs for more information."
href = "https://www.vinyldns.io/portal/manual-review-scheduling"
},
{
status = "Scheduled"
alertType = "info"
text = "If there are any changes in your scheduled DNS Change with a status of Needs Review they will be addressed prior to the scheduled date and time."
}
]
play.modules.enabled += "modules.VinylDNSModule"
# base version this image is built on

View File

@ -166,3 +166,16 @@ ul.bar_tabs>li a {
background-color: #e6e6e6!important;
border: 1px solid #adadad!important;
}
.alert-danger a, .alert-warning a, .alert-info a, .alert-success a {
font-weight: 600;
text-decoration: underline;
}
.alert-danger a, .alert-warning a, .alert-info a {
color: #E9EDEF!important;
}
.alert-success a {
color: #fff!important;
}

View File

@ -14,4 +14,4 @@
* limitations under the License.
*/
angular.module('directives.module', ['directives.validations.module', 'directives.modals.module', 'directives.notifications.module']);
angular.module('directives.module', ['directives.validations.module', 'directives.modals.module', 'directives.notifications.module', 'directives.notices.module']);

View File

@ -0,0 +1,27 @@
/*
* Copyright 2018 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
angular.module('directives.notices.module', [])
.directive('notice', function($timeout) {
return {
restrict: 'E',
replace: true,
scope: {
ngModel: '='
},
templateUrl: "/public/templates/notice.html"
}
});

View File

@ -36,6 +36,7 @@
if (response.data.reviewTimestamp) {
$scope.batch.reviewTimestamp = utilityService.formatDateTime(response.data.reviewTimestamp);
}
$scope.notice = $scope.notices.find(notice => notice['status'] == $scope.batch.status)
}
return dnsChangeService

View File

@ -0,0 +1,4 @@
<div class="alert" ng-class="'alert-'+ngModel.alertType" role="alert">
{{ngModel.text}}
<a ng-if="ngModel.href && ngModel.hrefText" href="{{ngModel.href}}" target="_blank">{{ngModel.hrefText}}</a>
</div>

View File

@ -0,0 +1,219 @@
/*
* Copyright 2018 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package models
import com.typesafe.config.ConfigException
import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import play.api.Configuration
import play.api.libs.json.{JsValue, Json}
class DnsChangeNoticesSpec extends Specification with Mockito {
"DnsChangeNotices" should {
"load valid notices from config" in {
val config = Map(
"dns-change-notices" -> List(
Map(
"status" -> "Cancelled",
"alertType" -> "info",
"text" -> "No href or hrefText provided."
),
Map(
"status" -> "Complete",
"alertType" -> "success",
"text" -> "href and hrefText provided.",
"href" -> "http://example.com",
"hrefText" -> "See more."
),
Map(
"status" -> "PendingReview",
"alertType" -> "warning",
"text" -> "No hrefText provided.",
"href" -> "http://example.com"
),
Map(
"status" -> "Failed",
"alertType" -> "danger",
"text" -> "No href provided.",
"hrefText" -> "See more."
)
)
)
val configFormatted = Json.toJson(
List(
Map(
"status" -> "Cancelled",
"alertType" -> "info",
"text" -> "No href or hrefText provided.",
"href" -> "",
"hrefText" -> ""
),
Map(
"status" -> "Complete",
"alertType" -> "success",
"text" -> "href and hrefText provided.",
"href" -> "http://example.com",
"hrefText" -> "See more."
),
Map(
"status" -> "PendingReview",
"alertType" -> "warning",
"text" -> "No hrefText provided.",
"href" -> "http://example.com",
"hrefText" -> ""
),
Map(
"status" -> "Failed",
"alertType" -> "danger",
"text" -> "No href provided.",
"href" -> "",
"hrefText" -> "See more."
)
)
)
val dnsChangeNotices = Configuration.from(config).get[DnsChangeNotices]("dns-change-notices")
dnsChangeNotices.notices must beEqualTo(configFormatted)
}
"load valid notices from config" in {
val config = Map(
"dns-change-notices" -> List()
)
val configFormatted = Json.arr()
val dnsChangeNotices = Configuration.from(config).get[DnsChangeNotices]("dns-change-notices")
dnsChangeNotices.notices must beEqualTo(configFormatted)
}
"error if no dns-change-notices key is in the config" in {
Configuration
.from(Map())
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.Missing](
"No configuration setting found for key 'dns-change-notices'"
)
}
"error if the dns-change-notices value is not a list" in {
Configuration
.from(Map("dns-change-notices" -> "invalid"))
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.WrongType]("dns-change-notices has type STRING rather than LIST")
}
"error if no text value is given" in {
val config = Map(
"dns-change-notices" -> List(
Map(
"status" -> "Cancelled",
"alertType" -> "info"
)
)
)
Configuration
.from(config)
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.Missing]("No configuration setting found for key 'text'")
}
"error if the given text value is not a string" in {
val config = Map(
"dns-change-notices" -> List(
Map(
"text" -> List("all done."),
"status" -> "Cancelled",
"alertType" -> "info"
)
)
)
Configuration
.from(config)
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.WrongType]("text has type LIST rather than STRING")
}
"error if no status is given" in {
val config = Map(
"dns-change-notices" -> List(
Map(
"alertType" -> "info",
"text" -> "Invalid status value"
)
)
)
Configuration
.from(config)
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.Missing]("No configuration setting found for key 'status'")
}
"error if an invalid status is given" in {
val config = Map(
"dns-change-notices" -> List(
Map(
"status" -> "Invalid",
"alertType" -> "info",
"text" -> "Invalid status value"
)
)
)
Configuration
.from(config)
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.BadValue]
}
"error if no alertType is given" in {
val config = Map(
"dns-change-notices" -> List(
Map(
"status" -> "Complete",
"text" -> "Invalid alertType value"
)
)
)
Configuration
.from(config)
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.Missing]("No configuration setting found for key 'alertType'")
}
"error if an invalid status is given" in {
val config = Map(
"dns-change-notices" -> List(
Map(
"status" -> "Invalid",
"alertType" -> "veryBad",
"text" -> "Invalid status value"
)
)
)
Configuration
.from(config)
.get[DnsChangeNotices]("dns-change-notices") must
throwA[ConfigException.BadValue]
}
}
}

View File

@ -17,6 +17,7 @@ object Dependencies {
lazy val jaxbV = "2.3.0"
lazy val ip4sV = "1.1.1"
lazy val fs2V = "2.0.1"
lazy val ficusV = "1.4.3"
lazy val apiDependencies = Seq(
"com.typesafe.akka" %% "akka-http" % akkaHttpV,
@ -50,7 +51,7 @@ object Dependencies {
"com.47deg" %% "github4s" % "0.18.6",
"com.comcast" %% "ip4s-core" % ip4sV,
"com.comcast" %% "ip4s-cats" % ip4sV,
"com.iheart" %% "ficus" % "1.4.3",
"com.iheart" %% "ficus" % ficusV,
"com.sun.mail" % "javax.mail" % "1.6.2",
"javax.mail" % "javax.mail-api" % "1.6.2",
"com.amazonaws" % "aws-java-sdk-sns" % awsV withSources()
@ -118,6 +119,7 @@ object Dependencies {
"com.nimbusds" % "oauth2-oidc-sdk" % "6.5",
"com.nimbusds" % "nimbus-jose-jwt" % "7.0",
"co.fs2" %% "fs2-core" % fs2V,
"de.leanovate.play-mockws" %% "play-mockws" % "2.7.1" % "test"
"de.leanovate.play-mockws" %% "play-mockws" % "2.7.1" % "test",
"com.iheart" %% "ficus" % ficusV
)
}