From e3da818101adc59e7dac951a2a3aa64ecb70e637 Mon Sep 17 00:00:00 2001 From: Britney Wright Date: Mon, 11 Nov 2019 16:09:57 -0500 Subject: [PATCH] DNS Change detail notices (#901) --- .../app/controllers/FrontendController.scala | 5 +- .../portal/app/models/DnsChangeNotices.scala | 69 ++++++ .../dnsChanges/dnsChangeDetail.scala.html | 7 +- modules/portal/app/views/main.scala.html | 1 + modules/portal/conf/reference.conf | 20 ++ modules/portal/public/css/theme-overrides.css | 13 ++ .../public/lib/directives/directives.js | 2 +- .../lib/directives/directives.notices.js | 27 +++ .../dns-change-detail.controller.js | 1 + modules/portal/public/templates/notice.html | 4 + .../test/models/DnsChangeNoticesSpec.scala | 219 ++++++++++++++++++ project/Dependencies.scala | 6 +- 12 files changed, 367 insertions(+), 7 deletions(-) create mode 100644 modules/portal/app/models/DnsChangeNotices.scala create mode 100644 modules/portal/public/lib/directives/directives.notices.js create mode 100644 modules/portal/public/templates/notice.html create mode 100644 modules/portal/test/models/DnsChangeNoticesSpec.scala diff --git a/modules/portal/app/controllers/FrontendController.scala b/modules/portal/app/controllers/FrontendController.scala index 5ec9f1bd1..416595922 100644 --- a/modules/portal/app/controllers/FrontendController.scala +++ b/modules/portal/app/controllers/FrontendController.scala @@ -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) ) ) } diff --git a/modules/portal/app/models/DnsChangeNotices.scala b/modules/portal/app/models/DnsChangeNotices.scala new file mode 100644 index 000000000..64b74ffb9 --- /dev/null +++ b/modules/portal/app/models/DnsChangeNotices.scala @@ -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 +} diff --git a/modules/portal/app/views/dnsChanges/dnsChangeDetail.scala.html b/modules/portal/app/views/dnsChanges/dnsChangeDetail.scala.html index 83ff8581a..3bbeaa7d2 100644 --- a/modules/portal/app/views/dnsChanges/dnsChangeDetail.scala.html +++ b/modules/portal/app/views/dnsChanges/dnsChangeDetail.scala.html @@ -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 = { -
+
+ + +

ID: {{batch.id}}

diff --git a/modules/portal/app/views/main.scala.html b/modules/portal/app/views/main.scala.html index 26750bc33..da2e7a10d 100644 --- a/modules/portal/app/views/main.scala.html +++ b/modules/portal/app/views/main.scala.html @@ -169,6 +169,7 @@ + diff --git a/modules/portal/conf/reference.conf b/modules/portal/conf/reference.conf index ef6e50cf9..eebdfe7ca 100644 --- a/modules/portal/conf/reference.conf +++ b/modules/portal/conf/reference.conf @@ -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 diff --git a/modules/portal/public/css/theme-overrides.css b/modules/portal/public/css/theme-overrides.css index 4eca3eb02..136ac2214 100644 --- a/modules/portal/public/css/theme-overrides.css +++ b/modules/portal/public/css/theme-overrides.css @@ -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; +} diff --git a/modules/portal/public/lib/directives/directives.js b/modules/portal/public/lib/directives/directives.js index c7f1ec50d..7eb90a871 100644 --- a/modules/portal/public/lib/directives/directives.js +++ b/modules/portal/public/lib/directives/directives.js @@ -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']); diff --git a/modules/portal/public/lib/directives/directives.notices.js b/modules/portal/public/lib/directives/directives.notices.js new file mode 100644 index 000000000..f7bc7a1ea --- /dev/null +++ b/modules/portal/public/lib/directives/directives.notices.js @@ -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" + } + }); diff --git a/modules/portal/public/lib/dns-change/dns-change-detail.controller.js b/modules/portal/public/lib/dns-change/dns-change-detail.controller.js index 292243e25..024177d5c 100644 --- a/modules/portal/public/lib/dns-change/dns-change-detail.controller.js +++ b/modules/portal/public/lib/dns-change/dns-change-detail.controller.js @@ -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 diff --git a/modules/portal/public/templates/notice.html b/modules/portal/public/templates/notice.html new file mode 100644 index 000000000..a18feeb9d --- /dev/null +++ b/modules/portal/public/templates/notice.html @@ -0,0 +1,4 @@ + diff --git a/modules/portal/test/models/DnsChangeNoticesSpec.scala b/modules/portal/test/models/DnsChangeNoticesSpec.scala new file mode 100644 index 000000000..c972cc458 --- /dev/null +++ b/modules/portal/test/models/DnsChangeNoticesSpec.scala @@ -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] + } + } +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index d1a84580f..83f514db8 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -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 ) }