diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml new file mode 100644 index 000000000..c7cf65200 --- /dev/null +++ b/.github/workflows/release-beta.yml @@ -0,0 +1,149 @@ +name: VinylDNS Beta Release +concurrency: + cancel-in-progress: true + group: "release" + +defaults: + run: + shell: bash + +on: + workflow_dispatch: + inputs: + verify-first: + description: 'Verify First?' + required: true + default: 'true' + create-gh-release: + description: 'Create a GitHub Release?' + required: true + default: 'true' + publish-images: + description: 'Publish Docker Images?' + required: true + default: 'true' + pre-release: + description: 'Is this a pre-release?' + required: true + default: 'true' + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + verify: + name: Verify Release + runs-on: ubuntu-latest + + steps: + - name: Checkout current branch + if: github.event.inputs.verify-first == 'true' + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run Tests + id: verify + if: github.event.inputs.verify-first == 'true' + run: cd build/ && ./assemble_api.sh && ./run_all_tests.sh + + create-gh-release: + name: Create GitHub Release + needs: verify + runs-on: ubuntu-latest + if: github.event.inputs.create-gh-release == 'true' + permissions: + contents: write + + steps: + - name: Checkout current branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build Artifacts + id: build + run: cd build/ && ./assemble_api.sh && ./assemble_portal.sh + + - name: Get Version + id: get-version + run: echo "::set-output name=vinyldns_version::$(awk -F'"' '{print $2}' ./version.sbt)" + + - name: Create GitHub Release + id: create_release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ steps.get-version.outputs.vinyldns_version }} + generate_release_notes: true + files: artifacts/* + prerelease: ${{ github.event.inputs['pre-release'] == 'true' }} + + docker-release-api: + name: Release API Docker Image + needs: [ verify, create-gh-release ] + runs-on: ubuntu-latest + if: github.event.inputs.publish-images == 'true' + + steps: + - name: Get Version + id: get-version + run: echo "::set-output name=vinyldns_version::$(curl -s https://api.github.com/repos/vinyldns/vinyldns/releases | jq -rc '.[0].tag_name')" + + - name: Checkout current branch (full) + uses: actions/checkout@v4 + with: + ref: ${{ steps.get-version.outputs.vinyldns_version }} + fetch-depth: 0 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Import Content Trust Key + run: docker trust key load <(echo "${SIGNING_KEY}") --name vinyldns_svc + env: + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }} + + # This will publish the latest release + - name: Publish API Docker Image + run: make -C build/docker/api publish + env: + DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }} + + docker-release-portal: + name: Release Portal Docker Image + needs: [ verify, create-gh-release ] + runs-on: ubuntu-latest + if: github.event.inputs.publish-images == 'true' + + steps: + - name: Get Version + id: get-version + run: echo "::set-output name=vinyldns_version::$(curl -s https://api.github.com/repos/vinyldns/vinyldns/releases | jq -rc '.[0].tag_name')" + + - name: Checkout current branch (full) + uses: actions/checkout@v4 + with: + ref: ${{ steps.get-version.outputs.vinyldns_version }} + fetch-depth: 0 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Import Content Trust Key + run: docker trust key load <(echo "${SIGNING_KEY}") --name vinyldns_svc + env: + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }} + + # This will publish the latest release + - name: Publish Portal Docker Image + run: make -C build/docker/portal publish + env: + DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1ce26c22..4a4113686 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: steps: - name: Checkout current branch if: github.event.inputs.verify-first == 'true' - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -53,7 +53,7 @@ jobs: steps: - name: Checkout current branch - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -67,7 +67,7 @@ jobs: - name: Create GitHub Release id: create_release - uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5 # v0.1.14 + uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.get-version.outputs.vinyldns_version }} generate_release_notes: true @@ -85,13 +85,13 @@ jobs: run: echo "::set-output name=vinyldns_version::$(curl -s https://api.github.com/repos/vinyldns/vinyldns/releases | jq -rc '.[0].tag_name')" - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: ref: ${{ steps.get-version.outputs.vinyldns_version }} fetch-depth: 0 - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_TOKEN }} @@ -120,13 +120,13 @@ jobs: run: echo "::set-output name=vinyldns_version::$(curl -s https://api.github.com/repos/vinyldns/vinyldns/releases | jq -rc '.[0].tag_name')" - name: Checkout current branch (full) - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: ref: ${{ steps.get-version.outputs.vinyldns_version }} fetch-depth: 0 - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_TOKEN }} diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 126b64c90..c46ac0f20 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -31,28 +31,28 @@ jobs: - name: Codecov id: codecov0 - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 continue-on-error: true with: token: ${{ secrets.CODECOV_TOKEN }} - name: Codecov Retry id: codecov1 if: steps.codecov0.outcome=='failure' - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 continue-on-error: true with: token: ${{ secrets.CODECOV_TOKEN }} - name: Codecov Retry 2 id: codecov2 if: steps.codecov1.outcome=='failure' - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 continue-on-error: true with: token: ${{ secrets.CODECOV_TOKEN }} - name: Codecov Retry 3 id: codecov3 if: steps.codecov2.outcome=='failure' - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 continue-on-error: true with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/AUTHORS.md b/AUTHORS.md index 2e713ac03..45040ece9 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -21,6 +21,7 @@ in any way, but do not see your name here, please open a PR to add yourself (in - Joe Crowe - Jearvon Dharrie - Andrew Dunn +- Josh Edwards - Ryan Emerle - David Grizzanti - Alejandro Guirao @@ -41,8 +42,9 @@ in any way, but do not see your name here, please open a PR to add yourself (in - Khalid Reid - Timo Schmid - Trent Schmidt -- Nick Spadaccino +- Arpit Shah - Ghafar Shah +- Nick Spadaccino - Rebecca Star - Jess Stodola - Juan Valencia diff --git a/README.md b/README.md index 1759bc105..ee01cc4cc 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,8 @@ See the [Contributing Guide](CONTRIBUTING.md). The current maintainers (people who can merge pull requests) are: - Ryan Emerle ([@remerle](https://github.com/remerle)) -- Sriram Ramakrishnan ([@sramakr](https://github.com/sramakr)) +- Arpit Shah ([@arpit4ever](https://github.com/arpit4ever)) +- Nick Spadaccino ([@nspadaccino](https://github.com/nspadaccino)) - Jim Wakemen ([@jwakemen](https://github.com/jwakemen)) See [AUTHORS.md](AUTHORS.md) for the full list of contributors to VinylDNS. diff --git a/build/docker/api/application.conf b/build/docker/api/application.conf index f3d2e8ef2..6a5dd8732 100644 --- a/build/docker/api/application.conf +++ b/build/docker/api/application.conf @@ -351,6 +351,10 @@ akka.http { # Set to `infinite` to disable. bind-timeout = 5s + # A default request timeout is applied globally to all routes and can be configured using the + # akka.http.server.request-timeout setting (which defaults to 20 seconds). + # request-timeout = 60s + # Show verbose error messages back to the client verbose-error-messages = on } diff --git a/build/docker/api/logback.xml b/build/docker/api/logback.xml index 6b90f7dcb..656ee3ab1 100644 --- a/build/docker/api/logback.xml +++ b/build/docker/api/logback.xml @@ -14,7 +14,7 @@ - + diff --git a/build/docker/portal/logback.xml b/build/docker/portal/logback.xml index b7e61088c..42e9a8b80 100644 --- a/build/docker/portal/logback.xml +++ b/build/docker/portal/logback.xml @@ -15,7 +15,7 @@ - + diff --git a/modules/api/src/it/resources/application.conf b/modules/api/src/it/resources/application.conf index dd3b7006e..f96a2c5e5 100644 --- a/modules/api/src/it/resources/application.conf +++ b/modules/api/src/it/resources/application.conf @@ -348,6 +348,10 @@ akka.http { # Set to `infinite` to disable. bind-timeout = 5s + # A default request timeout is applied globally to all routes and can be configured using the + # akka.http.server.request-timeout setting (which defaults to 20 seconds). + # request-timeout = 60s + # Show verbose error messages back to the client verbose-error-messages = on } diff --git a/modules/api/src/main/resources/application.conf b/modules/api/src/main/resources/application.conf index c1ee28ca9..68b519c26 100644 --- a/modules/api/src/main/resources/application.conf +++ b/modules/api/src/main/resources/application.conf @@ -367,6 +367,10 @@ akka.http { # Set to `infinite` to disable. bind-timeout = 5s + # A default request timeout is applied globally to all routes and can be configured using the + # akka.http.server.request-timeout setting (which defaults to 20 seconds). + # request-timeout = 60s + # Show verbose error messages back to the client verbose-error-messages = on } diff --git a/modules/api/src/main/resources/logback.xml b/modules/api/src/main/resources/logback.xml index 688a07b6b..4e4f1f6aa 100644 --- a/modules/api/src/main/resources/logback.xml +++ b/modules/api/src/main/resources/logback.xml @@ -7,7 +7,7 @@ - + diff --git a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeConverter.scala b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeConverter.scala index 29dc8477c..eb3c9dc06 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeConverter.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeConverter.scala @@ -30,7 +30,6 @@ import vinyldns.core.domain.zone.Zone import vinyldns.core.domain.batch._ import vinyldns.core.domain.record.RecordType.{RecordType, UNKNOWN} import vinyldns.core.queue.MessageQueue -import java.net.InetAddress class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue: MessageQueue) extends BatchChangeConverterAlgebra { @@ -52,17 +51,16 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue: s"Converting BatchChange [${batchChange.id}] with SingleChanges [${batchChange.changes.map(_.id)}]" ) for { - updatedBatchChange <- updateBatchChange(batchChange, groupedChanges).toRightBatchResult recordSetChanges <- createRecordSetChangesForBatch( - updatedBatchChange.changes, + batchChange.changes, existingZones, groupedChanges, batchChange.userId, ownerGroupId ).toRightBatchResult - _ <- allChangesWereConverted(updatedBatchChange.changes, recordSetChanges) + _ <- allChangesWereConverted(batchChange.changes, recordSetChanges) _ <- batchChangeRepo - .save(updatedBatchChange) + .save(batchChange) .toBatchResult // need to save the change before queueing, backend processing expects the changes to exist queued <- putChangesOnQueue(recordSetChanges, batchChange.id) changeToStore = updateWithQueueingFailures(batchChange, queued) @@ -129,7 +127,7 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue: change match { case _: SingleDeleteRRSetChange if change.recordSetId.isEmpty => // Mark as Complete since we don't want to throw it as an error - change.withDoesNotExistMessage(nonExistentRecordDeleteMessage) + change.withDoesNotExistMessage case _ => // Failure here means there was a message queue issue for this change change.withFailureMessage(failedMessage) @@ -142,35 +140,12 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue: def storeQueuingFailures(batchChange: BatchChange): BatchResult[Unit] = { // Update if Single change is Failed or if a record that does not exist is deleted val failedAndNotExistsChanges = batchChange.changes.collect { - case change if change.status == SingleChangeStatus.Failed || change.systemMessage.contains(nonExistentRecordDeleteMessage) => change + case change if change.status == SingleChangeStatus.Failed || change.systemMessage.contains(nonExistentRecordDeleteMessage) || change.systemMessage.contains(nonExistentRecordDataDeleteMessage) => change } - batchChangeRepo.updateSingleChanges(failedAndNotExistsChanges).as(()) + val storeChanges = batchChangeRepo.updateSingleChanges(failedAndNotExistsChanges).as(()) + storeChanges }.toBatchResult - def matchRecordData(existingRecordSetData: List[RecordData], recordData: RecordData): Boolean = - existingRecordSetData.exists { rd => - (rd, recordData) match { - case (AAAAData(rdAddress), AAAAData(proposedAddress)) => - InetAddress.getByName(proposedAddress).getHostName == InetAddress - .getByName(rdAddress) - .getHostName - case _ => rd == recordData - } - } - - def updateBatchChange(batchChange: BatchChange, groupedChanges: ChangeForValidationMap): BatchChange = { - // Update system message to be display the information if record data doesn't exist for the delete request - val singleChanges = batchChange.changes.map { - case change@(sd: SingleDeleteRRSetChange) => - if (sd.recordData.isDefined && !groupedChanges.getExistingRecordSet(change.recordKey.get).exists(rs => matchRecordData(rs.records, sd.recordData.get))) { - sd.copy(systemMessage = Some(nonExistentRecordDataDeleteMessage)) - } - else change - case change => change - } - batchChange.copy(changes = singleChanges) - } - def createRecordSetChangesForBatch( changes: List[SingleChange], existingZones: ExistingZones, diff --git a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeProtocol.scala b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeProtocol.scala index cef50fbd1..6b3b98364 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeProtocol.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeProtocol.scala @@ -44,12 +44,14 @@ object BatchChangeInput { sealed trait ChangeInput { val inputName: String val typ: RecordType + val systemMessage: Option[String] def asNewStoredChange(errors: NonEmptyList[DomainValidationError], defaultTtl: Long): SingleChange } final case class AddChangeInput( inputName: String, typ: RecordType, + systemMessage: Option[String], ttl: Option[Long], record: RecordData ) extends ChangeInput { @@ -68,7 +70,7 @@ final case class AddChangeInput( knownTtl, record, SingleChangeStatus.NeedsReview, - None, + systemMessage, None, None, errors.toList.map(SingleChangeError(_)) @@ -79,6 +81,7 @@ final case class AddChangeInput( final case class DeleteRRSetChangeInput( inputName: String, typ: RecordType, + systemMessage: Option[String], record: Option[RecordData] ) extends ChangeInput { def asNewStoredChange( @@ -93,7 +96,7 @@ final case class DeleteRRSetChangeInput( typ, record, SingleChangeStatus.NeedsReview, - None, + systemMessage, None, None, errors.toList.map(SingleChangeError(_)) @@ -104,6 +107,7 @@ object AddChangeInput { def apply( inputName: String, typ: RecordType, + systemMessage: Option[String], ttl: Option[Long], record: RecordData ): AddChangeInput = { @@ -111,28 +115,29 @@ object AddChangeInput { case PTR => inputName case _ => ensureTrailingDot(inputName) } - new AddChangeInput(transformName, typ, ttl, record) + new AddChangeInput(transformName, typ, systemMessage, ttl, record) } def apply(sc: SingleAddChange): AddChangeInput = - AddChangeInput(sc.inputName, sc.typ, Some(sc.ttl), sc.recordData) + AddChangeInput(sc.inputName, sc.typ, sc.systemMessage, Some(sc.ttl), sc.recordData) } object DeleteRRSetChangeInput { def apply( inputName: String, typ: RecordType, + systemMessage: Option[String], record: Option[RecordData] = None ): DeleteRRSetChangeInput = { val transformName = typ match { case PTR => inputName case _ => ensureTrailingDot(inputName) } - new DeleteRRSetChangeInput(transformName, typ, record) + new DeleteRRSetChangeInput(transformName, typ, systemMessage, record) } def apply(sc: SingleDeleteRRSetChange): DeleteRRSetChangeInput = - DeleteRRSetChangeInput(sc.inputName, sc.typ, sc.recordData) + DeleteRRSetChangeInput(sc.inputName, sc.typ, sc.systemMessage, sc.recordData) } object ChangeInputType extends Enumeration { diff --git a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeValidations.scala b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeValidations.scala index 342128bd2..7b24c40a5 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeValidations.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchChangeValidations.scala @@ -16,7 +16,6 @@ package vinyldns.api.domain.batch -import java.net.InetAddress import java.time.Instant import java.time.temporal.ChronoUnit import cats.data._ @@ -315,16 +314,34 @@ class BatchChangeValidations( else ().validNel - def matchRecordData(existingRecordSetData: List[RecordData], recordData: RecordData): Boolean = - existingRecordSetData.exists { rd => - (rd, recordData) match { - case (AAAAData(rdAddress), AAAAData(proposedAddress)) => - InetAddress.getByName(proposedAddress).getHostName == InetAddress - .getByName(rdAddress) - .getHostName - case _ => rd == recordData - } + def matchRecordData(existingRecordSetData: List[RecordData], recordData: RecordData): Boolean = { + existingRecordSetData.par.exists { rd => + rd == recordData } + } + + def ensureRecordExists( + change: ChangeForValidation, + groupedChanges: ChangeForValidationMap + ): Boolean = { + change match { + // For DeleteRecord inputs, need to verify that the record data actually exists + case DeleteRRSetChangeForValidation(_, _, DeleteRRSetChangeInput(_, _, _, Some(recordData))) + if !groupedChanges + .getExistingRecordSet(change.recordKey) + .exists(rs => matchRecordData(rs.records, recordData)) => + false + case _ => + true + } + } + + def updateSystemMessage(changeInput: ChangeInput, systemMessage: String): ChangeInput = { + changeInput match { + case dci: DeleteRRSetChangeInput => dci.copy(systemMessage = Some(systemMessage)) + case _ => changeInput + } + } def validateDeleteWithContext( change: ChangeForValidation, @@ -333,26 +350,37 @@ class BatchChangeValidations( isApproved: Boolean ): SingleValidation[ChangeForValidation] = { - // To handle add and delete for the record with same record data is present in the batch + val nonExistentRecordDeleteMessage = "This record does not exist. No further action is required." + val nonExistentRecordDataDeleteMessage = "Record data entered does not exist. No further action is required." + val recordData = change match { case AddChangeForValidation(_, _, inputChange, _, _) => inputChange.record.toString - case DeleteRRSetChangeForValidation(_, _, inputChange) => if(inputChange.record.isDefined) inputChange.record.get.toString else "" + case DeleteRRSetChangeForValidation(_, _, inputChange) => inputChange.record.map(_.toString).getOrElse("") } val addInBatch = groupedChanges.getProposedAdds(change.recordKey) - val isSameRecordUpdateInBatch = if(recordData.nonEmpty){ - if(addInBatch.contains(RecordData.fromString(recordData, change.inputChange.typ).get)) true else false - } else false + val isSameRecordUpdateInBatch = recordData.nonEmpty && addInBatch.contains(RecordData.fromString(recordData, change.inputChange.typ).get) - val validations = - groupedChanges.getExistingRecordSet(change.recordKey) match { - case Some(rs) => - userCanDeleteRecordSet(change, auth, rs.ownerGroupId, rs.records) |+| - zoneDoesNotRequireManualReview(change, isApproved) - case None => - if(isSameRecordUpdateInBatch) InvalidUpdateRequest(change.inputChange.inputName).invalidNel else ().validNel - } - validations.map(_ => change) + // Perform the system message update based on the condition + val updatedChange = if (groupedChanges.getExistingRecordSet(change.recordKey).isEmpty && !isSameRecordUpdateInBatch) { + val updatedChangeInput = updateSystemMessage(change.inputChange, nonExistentRecordDeleteMessage) + change.withUpdatedInputChange(updatedChangeInput) + } else if (!ensureRecordExists(change, groupedChanges)) { + val updatedChangeInput = updateSystemMessage(change.inputChange, nonExistentRecordDataDeleteMessage) + change.withUpdatedInputChange(updatedChangeInput) + } else { + change + } + + val validations = groupedChanges.getExistingRecordSet(updatedChange.recordKey) match { + case Some(rs) => + userCanDeleteRecordSet(updatedChange, auth, rs.ownerGroupId, rs.records) |+| + zoneDoesNotRequireManualReview(updatedChange, isApproved) + case None => + if (isSameRecordUpdateInBatch) InvalidUpdateRequest(updatedChange.inputChange.inputName).invalidNel else ().validNel + } + + validations.map(_ => updatedChange) } def validateAddUpdateWithContext( @@ -397,28 +425,40 @@ class BatchChangeValidations( isApproved: Boolean ): SingleValidation[ChangeForValidation] = { + val nonExistentRecordDeleteMessage = "This record does not exist. No further action is required." + val nonExistentRecordDataDeleteMessage = "Record data entered does not exist. No further action is required." + // To handle add and delete for the record with same record data is present in the batch val recordData = change match { case AddChangeForValidation(_, _, inputChange, _, _) => inputChange.record.toString - case DeleteRRSetChangeForValidation(_, _, inputChange) => if(inputChange.record.isDefined) inputChange.record.get.toString else "" + case DeleteRRSetChangeForValidation(_, _, inputChange) => inputChange.record.map(_.toString).getOrElse("") } val addInBatch = groupedChanges.getProposedAdds(change.recordKey) - val isSameRecordUpdateInBatch = if(recordData.nonEmpty){ - if(addInBatch.contains(RecordData.fromString(recordData, change.inputChange.typ).get)) true else false - } else false + val isSameRecordUpdateInBatch = recordData.nonEmpty && addInBatch.contains(RecordData.fromString(recordData, change.inputChange.typ).get) + + // Perform the system message update based on the condition + val updatedChange = if (groupedChanges.getExistingRecordSet(change.recordKey).isEmpty && !isSameRecordUpdateInBatch) { + val updatedChangeInput = updateSystemMessage(change.inputChange, nonExistentRecordDeleteMessage) + change.withUpdatedInputChange(updatedChangeInput) + } else if (!ensureRecordExists(change, groupedChanges)) { + val updatedChangeInput = updateSystemMessage(change.inputChange, nonExistentRecordDataDeleteMessage) + change.withUpdatedInputChange(updatedChangeInput) + } else { + change + } val validations = - groupedChanges.getExistingRecordSet(change.recordKey) match { + groupedChanges.getExistingRecordSet(updatedChange.recordKey) match { case Some(rs) => - val adds = groupedChanges.getProposedAdds(change.recordKey).toList - userCanUpdateRecordSet(change, auth, rs.ownerGroupId, adds) |+| - zoneDoesNotRequireManualReview(change, isApproved) + val adds = groupedChanges.getProposedAdds(updatedChange.recordKey).toList + userCanUpdateRecordSet(updatedChange, auth, rs.ownerGroupId, adds) |+| + zoneDoesNotRequireManualReview(updatedChange, isApproved) case None => - if(isSameRecordUpdateInBatch) InvalidUpdateRequest(change.inputChange.inputName).invalidNel else ().validNel + if(isSameRecordUpdateInBatch) InvalidUpdateRequest(updatedChange.inputChange.inputName).invalidNel else ().validNel } - validations.map(_ => change) + validations.map(_ => updatedChange) } def validateAddWithContext( diff --git a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchTransformations.scala b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchTransformations.scala index eb3953330..dbeafc2f6 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchTransformations.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/batch/BatchTransformations.scala @@ -16,7 +16,6 @@ package vinyldns.api.domain.batch -import java.net.InetAddress import java.util.UUID import vinyldns.api.domain.ReverseZoneHelpers @@ -24,7 +23,7 @@ import vinyldns.api.domain.batch.BatchChangeInterfaces.ValidatedBatch import vinyldns.api.domain.batch.BatchTransformations.LogicalChangeType.LogicalChangeType import vinyldns.api.backend.dns.DnsConversions.getIPv6FullReverseName import vinyldns.core.domain.batch._ -import vinyldns.core.domain.record.{AAAAData, RecordData, RecordSet, RecordSetChange} +import vinyldns.core.domain.record.{RecordData, RecordSet, RecordSetChange} import vinyldns.core.domain.record.RecordType._ import vinyldns.core.domain.zone.Zone import vinyldns.core.domain.record.RecordType.RecordType @@ -82,6 +81,7 @@ object BatchTransformations { val recordKey = RecordKey(zone.id, recordName, inputChange.typ) def asStoredChange(changeId: Option[String] = None): SingleChange def isAddChangeForValidation: Boolean + def withUpdatedInputChange(inputChange: ChangeInput): ChangeForValidation } object ChangeForValidation { @@ -118,7 +118,7 @@ object BatchTransformations { ttl, inputChange.record, SingleChangeStatus.Pending, - None, + inputChange.systemMessage, None, None, List.empty, @@ -127,6 +127,10 @@ object BatchTransformations { } def isAddChangeForValidation: Boolean = true + + def withUpdatedInputChange(inputChange: ChangeInput): ChangeForValidation = { + this.copy(inputChange = inputChange.asInstanceOf[AddChangeInput]) + } } final case class DeleteRRSetChangeForValidation( @@ -143,7 +147,7 @@ object BatchTransformations { inputChange.typ, inputChange.record, SingleChangeStatus.Pending, - None, + inputChange.systemMessage, None, None, List.empty, @@ -151,6 +155,10 @@ object BatchTransformations { ) def isAddChangeForValidation: Boolean = false + + def withUpdatedInputChange(inputChange: ChangeInput): ChangeForValidation = { + this.copy(inputChange = inputChange.asInstanceOf[DeleteRRSetChangeInput]) + } } final case class BatchConversionOutput( @@ -197,13 +205,6 @@ object BatchTransformations { } object ValidationChanges { - def matchRecordData(existingRecord: RecordData, recordData: String): Boolean = - existingRecord match { - case AAAAData(address) => - InetAddress.getByName(address).getHostName == - InetAddress.getByName(recordData).getHostName - case _ => false - } def apply( changes: List[ChangeForValidation], @@ -223,16 +224,11 @@ object BatchTransformations { case DeleteRRSetChangeForValidation( _, _, - DeleteRRSetChangeInput(_, AAAA, Some(AAAAData(address))) - ) => - existingRecords.filter(r => matchRecordData(r, address)) - case DeleteRRSetChangeForValidation( - _, - _, - DeleteRRSetChangeInput(_, _, Some(recordData)) + DeleteRRSetChangeInput(_, _, _, Some(recordData)) ) => Set(recordData) - case _: DeleteRRSetChangeForValidation => existingRecords + case _: DeleteRRSetChangeForValidation => + existingRecords } .toSet .flatten diff --git a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala index 4d85ebcbb..d61fac1e0 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetService.scala @@ -670,21 +670,19 @@ class RecordSetService( } yield change def listRecordSetChanges( - zoneId: Option[String] = None, + zoneId: String, startFrom: Option[Int] = None, maxItems: Int = 100, - fqdn: Option[String] = None, - recordType: Option[RecordType] = None, authPrincipal: AuthPrincipal ): Result[ListRecordSetChangesResponse] = for { - zone <- getZone(zoneId.get) + zone <- getZone(zoneId) _ <- canSeeZone(authPrincipal, zone).toResult recordSetChangesResults <- recordChangeRepository - .listRecordSetChanges(Some(zone.id), startFrom, maxItems, fqdn, recordType) + .listRecordSetChanges(Some(zone.id), startFrom, maxItems, None, None) .toResult[ListRecordSetChangesResults] recordSetChangesInfo <- buildRecordSetChangeInfo(recordSetChangesResults.items) - } yield ListRecordSetChangesResponse(zoneId.get, recordSetChangesResults, recordSetChangesInfo) + } yield ListRecordSetChangesResponse(zoneId, recordSetChangesResults, recordSetChangesInfo) def listRecordSetChangeHistory( zoneId: Option[String] = None, diff --git a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala index 10f1c3e5b..d21374b93 100644 --- a/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala +++ b/modules/api/src/main/scala/vinyldns/api/domain/record/RecordSetServiceAlgebra.scala @@ -101,11 +101,9 @@ trait RecordSetServiceAlgebra { ): Result[RecordSetChange] def listRecordSetChanges( - zoneId: Option[String], + zoneId: String, startFrom: Option[Int], maxItems: Int, - fqdn: Option[String], - recordType: Option[RecordType], authPrincipal: AuthPrincipal ): Result[ListRecordSetChangesResponse] diff --git a/modules/api/src/main/scala/vinyldns/api/engine/RecordSetChangeHandler.scala b/modules/api/src/main/scala/vinyldns/api/engine/RecordSetChangeHandler.scala index 51dde4b37..6fe54b1fb 100644 --- a/modules/api/src/main/scala/vinyldns/api/engine/RecordSetChangeHandler.scala +++ b/modules/api/src/main/scala/vinyldns/api/engine/RecordSetChangeHandler.scala @@ -38,6 +38,7 @@ object RecordSetChangeHandler extends TransactionProvider { private val outOfSyncFailureMessage: String = "This record set is out of sync with the DNS backend; sync this zone before attempting to update this record set." private val incompatibleRecordFailureMessage: String = "Incompatible record in DNS." private val syncZoneMessage: String = "This record set is out of sync with the DNS backend. Sync this zone before attempting to update this record set." + private val wrongRecordDataMessage: String = "The record data entered doesn't exist. Please enter the correct record data or leave the field empty if it's a delete operation." private val recordConflictMessage: String = "Conflict due to the record having the same name as an NS record in the same zone. Please create the record using the DNS service the NS record has been delegated to (ex. AWS r53), or use a different record name." final case class Requeue(change: RecordSetChange) extends Throwable @@ -392,12 +393,18 @@ object RecordSetChangeHandler extends TransactionProvider { case AlreadyApplied(_) => Completed(change.successful) case ReadyToApply(_) => Validated(change) case Failure(_, message) => - if(message == outOfSyncFailureMessage || message == incompatibleRecordFailureMessage){ + if(message == outOfSyncFailureMessage){ Completed( change.failed( syncZoneMessage ) ) + } else if (message == incompatibleRecordFailureMessage) { + Completed( + change.failed( + wrongRecordDataMessage + ) + ) } else if (message == "referral") { Completed( change.failed( diff --git a/modules/api/src/main/scala/vinyldns/api/route/BatchChangeJsonProtocol.scala b/modules/api/src/main/scala/vinyldns/api/route/BatchChangeJsonProtocol.scala index b2342dc53..d10885c13 100644 --- a/modules/api/src/main/scala/vinyldns/api/route/BatchChangeJsonProtocol.scala +++ b/modules/api/src/main/scala/vinyldns/api/route/BatchChangeJsonProtocol.scala @@ -88,6 +88,7 @@ trait BatchChangeJsonProtocol extends JsonValidation { ( (js \ "inputName").required[String]("Missing BatchChangeInput.changes.inputName"), recordType, + (js \ "systemMessage").optional[String], (js \ "ttl").optional[Long], recordType.andThen(extractRecord(_, js \ "record")) ).mapN(AddChangeInput.apply) @@ -114,6 +115,7 @@ trait BatchChangeJsonProtocol extends JsonValidation { ( (js \ "inputName").required[String]("Missing BatchChangeInput.changes.inputName"), recordType, + (js \ "systemMessage").optional[String], recordData ).mapN(DeleteRRSetChangeInput.apply) } diff --git a/modules/api/src/main/scala/vinyldns/api/route/BatchChangeRouting.scala b/modules/api/src/main/scala/vinyldns/api/route/BatchChangeRouting.scala index 13ee7b65b..d4578dc0a 100644 --- a/modules/api/src/main/scala/vinyldns/api/route/BatchChangeRouting.scala +++ b/modules/api/src/main/scala/vinyldns/api/route/BatchChangeRouting.scala @@ -16,13 +16,12 @@ package vinyldns.api.route -import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model._ import akka.http.scaladsl.server.{RejectionHandler, Route, ValidationRejection} -import vinyldns.api.config.LimitsConfig import org.slf4j.{Logger, LoggerFactory} -import vinyldns.api.config.ManualReviewConfig -import vinyldns.core.domain.batch._ +import vinyldns.api.config.{LimitsConfig, ManualReviewConfig} import vinyldns.api.domain.batch._ +import vinyldns.core.domain.batch._ class BatchChangeRoute( batchChangeService: BatchChangeServiceAlgebra, @@ -71,52 +70,52 @@ class BatchChangeRoute( } } } ~ - (get & monitor("Endpoint.listBatchChangeSummaries")) { - parameters( - "userName".as[String].?, - "dateTimeRangeStart".as[String].?, - "dateTimeRangeEnd".as[String].?, - "startFrom".as[Int].?, - "maxItems".as[Int].?(MAX_ITEMS_LIMIT), - "ignoreAccess".as[Boolean].?(false), - "approvalStatus".as[String].? - ) { - ( - userName: Option[String], - dateTimeRangeStart: Option[String], - dateTimeRangeEnd: Option[String], - startFrom: Option[Int], - maxItems: Int, - ignoreAccess: Boolean, - approvalStatus: Option[String] - ) => - { - val convertApprovalStatus = approvalStatus.flatMap(BatchChangeApprovalStatus.find) + (get & monitor("Endpoint.listBatchChangeSummaries")) { + parameters( + "userName".as[String].?, + "dateTimeRangeStart".as[String].?, + "dateTimeRangeEnd".as[String].?, + "startFrom".as[Int].?, + "maxItems".as[Int].?(MAX_ITEMS_LIMIT), + "ignoreAccess".as[Boolean].?(false), + "approvalStatus".as[String].? + ) { + ( + userName: Option[String], + dateTimeRangeStart: Option[String], + dateTimeRangeEnd: Option[String], + startFrom: Option[Int], + maxItems: Int, + ignoreAccess: Boolean, + approvalStatus: Option[String] + ) => + { + val convertApprovalStatus = approvalStatus.flatMap(BatchChangeApprovalStatus.find) - handleRejections(invalidQueryHandler) { - validate( - 0 < maxItems && maxItems <= MAX_ITEMS_LIMIT, - s"maxItems was $maxItems, maxItems must be between 1 and $MAX_ITEMS_LIMIT, inclusive." - ) { - authenticateAndExecute( - batchChangeService.listBatchChangeSummaries( - _, - userName, - dateTimeRangeStart, - dateTimeRangeEnd, - startFrom, - maxItems, - ignoreAccess, - convertApprovalStatus - ) - ) { summaries => - complete(StatusCodes.OK, summaries) - } + handleRejections(invalidQueryHandler) { + validate( + 0 < maxItems && maxItems <= MAX_ITEMS_LIMIT, + s"maxItems was $maxItems, maxItems must be between 1 and $MAX_ITEMS_LIMIT, inclusive." + ) { + authenticateAndExecute( + batchChangeService.listBatchChangeSummaries( + _, + userName, + dateTimeRangeStart, + dateTimeRangeEnd, + startFrom, + maxItems, + ignoreAccess, + convertApprovalStatus + ) + ) { summaries => + complete(StatusCodes.OK, summaries) } } } - } + } } + } } ~ path("zones" / "batchrecordchanges" / Segment) { id => (get & monitor("Endpoint.getBatchChange")) { diff --git a/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala b/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala index 1b289c21d..99c676813 100644 --- a/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala +++ b/modules/api/src/main/scala/vinyldns/api/route/RecordSetRouting.scala @@ -230,8 +230,8 @@ class RecordSetRoute( } ~ path("zones" / Segment / "recordsetchanges") { zoneId => (get & monitor("Endpoint.listRecordSetChanges")) { - parameters("startFrom".as[Int].?, "maxItems".as[Int].?(DEFAULT_MAX_ITEMS), "fqdn".as[String].?, "recordType".as[String].?) { - (startFrom: Option[Int], maxItems: Int, fqdn: Option[String], _: Option[String]) => + parameters("startFrom".as[Int].?, "maxItems".as[Int].?(DEFAULT_MAX_ITEMS)) { + (startFrom: Option[Int], maxItems: Int) => handleRejections(invalidQueryHandler) { validate( check = 0 < maxItems && maxItems <= DEFAULT_MAX_ITEMS, @@ -240,7 +240,7 @@ class RecordSetRoute( ) { authenticateAndExecute( recordSetService - .listRecordSetChanges(Some(zoneId), startFrom, maxItems, fqdn, None, _) + .listRecordSetChanges(zoneId, startFrom, maxItems, _) ) { changes => complete(StatusCodes.OK, changes) } diff --git a/modules/api/src/test/functional/requirements.txt b/modules/api/src/test/functional/requirements.txt index dcfb21080..1d71a609a 100644 --- a/modules/api/src/test/functional/requirements.txt +++ b/modules/api/src/test/functional/requirements.txt @@ -2,11 +2,11 @@ pyhamcrest==2.0.2 pytz>=2014 pytest==6.2.5 mock==4.0.3 -dnspython==2.1.0 +dnspython==2.6.1 boto3==1.18.51 botocore==1.21.51 -requests==2.31.0 +requests==2.32.3 pytest-xdist==2.4.0 python-dateutil==2.8.2 filelock==3.2.0 -pytest-custom_exit_code==0.3.0 \ No newline at end of file +pytest-custom_exit_code==0.3.0 diff --git a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeConverterSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeConverterSpec.scala index 0305d0830..43ab75ba9 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeConverterSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeConverterSpec.scala @@ -39,8 +39,6 @@ import vinyldns.core.domain.zone.Zone class BatchChangeConverterSpec extends AnyWordSpec with Matchers { private val nonExistentRecordDeleteMessage: String = "This record does not exist. " + "No further action is required." - private val nonExistentRecordDataDeleteMessage: String = "Record data entered does not exist. " + - "No further action is required." private def makeSingleAddChange( name: String, @@ -64,7 +62,7 @@ class BatchChangeConverterSpec extends AnyWordSpec with Matchers { ) } - private def makeSingleDeleteRRSetChange(name: String, typ: RecordType, zone: Zone = okZone) = { + private def makeSingleDeleteRRSetChange(name: String, typ: RecordType, zone: Zone = okZone, systemMessage: Option[String] = None) = { val fqdn = s"$name.${zone.name}" SingleDeleteRRSetChange( Some(zone.id), @@ -74,7 +72,7 @@ class BatchChangeConverterSpec extends AnyWordSpec with Matchers { typ, None, SingleChangeStatus.Pending, - None, + systemMessage, None, None ) @@ -88,18 +86,19 @@ class BatchChangeConverterSpec extends AnyWordSpec with Matchers { AddChangeForValidation( okZone, s"$recordName", - AddChangeInput(s"$recordName.ok.", typ, Some(123), recordData), + AddChangeInput(s"$recordName.ok.", typ, None, Some(123), recordData), 7200L ) private def makeDeleteRRSetChangeForValidation( recordName: String, - typ: RecordType = RecordType.A + typ: RecordType = RecordType.A, + systemMessage: Option[String] = None ): DeleteRRSetChangeForValidation = DeleteRRSetChangeForValidation( okZone, s"$recordName", - DeleteRRSetChangeInput(s"$recordName.ok.", typ) + DeleteRRSetChangeInput(s"$recordName.ok.", typ, systemMessage, None) ) private val addSingleChangesGood = List( @@ -165,19 +164,11 @@ class BatchChangeConverterSpec extends AnyWordSpec with Matchers { ) private val singleChangesOneDelete = List( - makeSingleDeleteRRSetChange("DoesNotExistToDelete", A) - ) - - private val singleChangesOneDeleteGood = List( - makeSingleDeleteRRSetChange("aToDelete", A).copy(recordData = Some(AData("2.3.4.6"))), + makeSingleDeleteRRSetChange("DoesNotExistToDelete", A, okZone, Some(nonExistentRecordDeleteMessage)) ) private val changeForValidationOneDelete = List( - makeDeleteRRSetChangeForValidation("DoesNotExistToDelete", A) - ) - - private val changeForValidationOneDeleteGood = List( - makeDeleteRRSetChangeForValidation("aToDelete", A) + makeDeleteRRSetChangeForValidation("DoesNotExistToDelete", A, Some(nonExistentRecordDeleteMessage)) ) private val singleChangesOneBad = List( @@ -621,45 +612,6 @@ class BatchChangeConverterSpec extends AnyWordSpec with Matchers { } } - "updateBatchChange" should { - "update the batch change system message when there is a delete request with non-existent record data" in { - val batchWithBadChange = - BatchChange( - okUser.id, - okUser.userName, - None, - Instant.now.truncatedTo(ChronoUnit.MILLIS), - singleChangesOneDeleteGood, - approvalStatus = BatchChangeApprovalStatus.AutoApproved - ) - val result = - underTest - .updateBatchChange( - batchWithBadChange, - ChangeForValidationMap(changeForValidationOneDeleteGood.map(_.validNel), existingRecordSets), - ) - - // validate the batch change returned - val receivedChange = result.changes(0) - receivedChange.systemMessage shouldBe Some(nonExistentRecordDataDeleteMessage) - result.changes(0) shouldBe singleChangesOneDeleteGood(0).copy(systemMessage = Some(nonExistentRecordDataDeleteMessage)) - } - } - - "matchRecordData" should { - "check if the record data given matches the record data present" in { - val recordData = List(AData("1.2.3.5"), AAAAData("caec:cec6:c4ef:bb7b:1a78:d055:216d:3a78")) - val result1 = underTest.matchRecordData(recordData, AData("1.2.3.5")) - result1 shouldBe true - val result2 = underTest.matchRecordData(recordData, AData("1.2.3.4")) - result2 shouldBe false - val result3 = underTest.matchRecordData(recordData, AAAAData("caec:cec6:c4ef:bb7b:1a78:d055:216d:3a78")) - result3 shouldBe true - val result4 = underTest.matchRecordData(recordData, AAAAData("abcd:cec6:c4ef:bb7b:1a78:d055:216d:3a78")) - result4 shouldBe false - } - } - "generateAddChange" should { val singleAddChange = makeSingleAddChange("shared-rs", AData("1.2.3.4"), A, sharedZone) val ownerGroupId = Some("some-owner-group-id") diff --git a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeInputSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeInputSpec.scala index fb614e387..b7d03b4fc 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeInputSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeInputSpec.scala @@ -31,16 +31,16 @@ class BatchChangeInputSpec extends AnyWordSpec with Matchers { "BatchChangeInput" should { "ensure trailing dot on A, AAAA, and CNAME fqdn" in { - val changeA = AddChangeInput("apex.test.com", A, Some(100), AData("1.1.1.1")) + val changeA = AddChangeInput("apex.test.com", A, None, Some(100), AData("1.1.1.1")) val changeAAAA = - AddChangeInput("aaaa.test.com", AAAA, Some(3600), AAAAData("1:2:3:4:5:6:7:8")) + AddChangeInput("aaaa.test.com", AAAA, None, Some(3600), AAAAData("1:2:3:4:5:6:7:8")) val changeCname = - AddChangeInput("cname.test.com", CNAME, Some(100), CNAMEData(Fqdn("testing.test.com"))) - val changeADotted = AddChangeInput("adot.test.com.", A, Some(100), AData("1.1.1.1")) + AddChangeInput("cname.test.com", CNAME, None, Some(100), CNAMEData(Fqdn("testing.test.com"))) + val changeADotted = AddChangeInput("adot.test.com.", A, None, Some(100), AData("1.1.1.1")) val changeAAAADotted = - AddChangeInput("aaaadot.test.com.", AAAA, Some(3600), AAAAData("1:2:3:4:5:6:7:8")) + AddChangeInput("aaaadot.test.com.", AAAA, None, Some(3600), AAAAData("1:2:3:4:5:6:7:8")) val changeCnameDotted = - AddChangeInput("cnamedot.test.com.", CNAME, Some(100), CNAMEData(Fqdn("testing.test.com."))) + AddChangeInput("cnamedot.test.com.", CNAME, None, Some(100), CNAMEData(Fqdn("testing.test.com."))) val input = BatchChangeInput( None, @@ -58,7 +58,7 @@ class BatchChangeInputSpec extends AnyWordSpec with Matchers { } "asNewStoredChange" should { "Convert an AddChangeInput into SingleAddChange" in { - val changeA = AddChangeInput("some.test.com", A, None, AData("1.1.1.1")) + val changeA = AddChangeInput("some.test.com", A, None, None, AData("1.1.1.1")) val converted = changeA.asNewStoredChange( NonEmptyList.of(ZoneDiscoveryError("test")), VinylDNSTestHelpers.defaultTtl @@ -80,7 +80,7 @@ class BatchChangeInputSpec extends AnyWordSpec with Matchers { asAdd.recordSetId shouldBe None } "Convert a DeleteChangeInput into SingleDeleteRRSetChange" in { - val changeA = DeleteRRSetChangeInput("some.test.com", A) + val changeA = DeleteRRSetChangeInput("some.test.com", A, None) val converted = changeA.asNewStoredChange( NonEmptyList.of(ZoneDiscoveryError("test")), VinylDNSTestHelpers.defaultTtl @@ -111,14 +111,14 @@ class BatchChangeInputSpec extends AnyWordSpec with Matchers { 1234, AData("1.2.3.4"), SingleChangeStatus.NeedsReview, - Some("msg"), + None, None, None, List(SingleChangeError(DomainValidationErrorType.ZoneDiscoveryError, "test err")) ) val expectedAddChange = - AddChangeInput("testRname.testZoneName.", A, Some(1234), AData("1.2.3.4")) + AddChangeInput("testRname.testZoneName.", A, None, Some(1234), AData("1.2.3.4")) val singleDelChange = SingleDeleteRRSetChange( Some("testZoneId"), @@ -128,14 +128,14 @@ class BatchChangeInputSpec extends AnyWordSpec with Matchers { A, None, SingleChangeStatus.NeedsReview, - Some("msg"), + None, None, None, List(SingleChangeError(DomainValidationErrorType.ZoneDiscoveryError, "test err")) ) val expectedDelChange = - DeleteRRSetChangeInput("testRname.testZoneName.", A) + DeleteRRSetChangeInput("testRname.testZoneName.", A, None) val change = BatchChange( "userId", diff --git a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeServiceSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeServiceSpec.scala index 843d9e28a..0cb423d64 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeServiceSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeServiceSpec.scala @@ -81,34 +81,36 @@ class BatchChangeServiceSpec ) private val ttl = Some(200L) - private val apexAddA = AddChangeInput("apex.test.com", RecordType.A, ttl, AData("1.1.1.1")) + private val apexAddA = AddChangeInput("apex.test.com", RecordType.A, None, ttl, AData("1.1.1.1")) private val nonApexAddA = - AddChangeInput("non-apex.test.com", RecordType.A, ttl, AData("1.1.1.1")) + AddChangeInput("non-apex.test.com", RecordType.A, None, ttl, AData("1.1.1.1")) private val onlyApexAddA = - AddChangeInput("only.apex.exists", RecordType.A, ttl, AData("1.1.1.1")) + AddChangeInput("only.apex.exists", RecordType.A, None, ttl, AData("1.1.1.1")) private val onlyBaseAddAAAA = - AddChangeInput("have.only.base", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")) - private val noZoneAddA = AddChangeInput("no.zone.match.", RecordType.A, ttl, AData("1.1.1.1")) + AddChangeInput("have.only.base", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")) + private val noZoneAddA = AddChangeInput("no.zone.match.", RecordType.A, None, ttl, AData("1.1.1.1")) private val dottedAddA = - AddChangeInput("dot.ted.apex.test.com", RecordType.A, ttl, AData("1.1.1.1")) + AddChangeInput("dot.ted.apex.test.com", RecordType.A, None, ttl, AData("1.1.1.1")) private val cnameAdd = - AddChangeInput("cname.test.com", RecordType.CNAME, ttl, CNAMEData(Fqdn("testing.test.com."))) + AddChangeInput("cname.test.com", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("testing.test.com."))) private val cnameApexAdd = - AddChangeInput("apex.test.com", RecordType.CNAME, ttl, CNAMEData(Fqdn("testing.test.com."))) + AddChangeInput("apex.test.com", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("testing.test.com."))) private val cnameReverseAdd = AddChangeInput( "cname.55.144.10.in-addr.arpa", RecordType.CNAME, + None, ttl, CNAMEData(Fqdn("testing.cname.com.")) ) - private val ptrAdd = AddChangeInput("10.144.55.11", RecordType.PTR, ttl, PTRData(Fqdn("ptr"))) - private val ptrAdd2 = AddChangeInput("10.144.55.255", RecordType.PTR, ttl, PTRData(Fqdn("ptr"))) + private val ptrAdd = AddChangeInput("10.144.55.11", RecordType.PTR, None, ttl, PTRData(Fqdn("ptr"))) + private val ptrAdd2 = AddChangeInput("10.144.55.255", RecordType.PTR, None, ttl, PTRData(Fqdn("ptr"))) private val ptrDelegatedAdd = - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("ptr"))) + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("ptr"))) private val ptrV6Add = AddChangeInput( "2001:0000:0000:0000:0000:ff00:0042:8329", RecordType.PTR, + None, ttl, PTRData(Fqdn("ptr")) ) @@ -489,6 +491,7 @@ class BatchChangeServiceSpec val ptr = AddChangeInput( "2001:0000:0000:0001:0000:ff00:0042:8329", RecordType.PTR, + None, ttl, PTRData(Fqdn("ptr")) ) @@ -520,6 +523,7 @@ class BatchChangeServiceSpec val ptr = AddChangeInput( "2001:0000:0000:0001:0000:ff00:0042:8329", RecordType.PTR, + None, ttl, PTRData(Fqdn("ptr")) ) @@ -577,12 +581,12 @@ class BatchChangeServiceSpec } "succeed with excluded TTL" in { - val noTtl = AddChangeInput("no-ttl-add.test.com", RecordType.A, None, AData("1.1.1.1")) + val noTtl = AddChangeInput("no-ttl-add.test.com", RecordType.A, None, None, AData("1.1.1.1")) val withTtl = - AddChangeInput("with-ttl-add-2.test.com", RecordType.A, Some(900), AData("1.1.1.1")) - val noTtlDel = DeleteRRSetChangeInput("non-apex.test.com.", RecordType.TXT) + AddChangeInput("with-ttl-add-2.test.com", RecordType.A, None, Some(900), AData("1.1.1.1")) + val noTtlDel = DeleteRRSetChangeInput("non-apex.test.com.", RecordType.TXT, None) val noTtlUpdate = - AddChangeInput("non-apex.test.com.", RecordType.TXT, None, TXTData("hello")) + AddChangeInput("non-apex.test.com.", RecordType.TXT, None, None, TXTData("hello")) val input = BatchChangeInput(None, List(noTtl, withTtl, noTtlDel, noTtlUpdate)) val result = underTest.applyBatchChange(input, auth, true).value.unsafeRunSync().toOption.get @@ -1172,7 +1176,7 @@ class BatchChangeServiceSpec "0.1.0.0.2.ip6.arpa." ) - val ptr = AddChangeInput(ip, RecordType.PTR, ttl, PTRData(Fqdn("ptr."))).validNel + val ptr = AddChangeInput(ip, RecordType.PTR, None, ttl, PTRData(Fqdn("ptr."))).validNel val underTestPTRZonesList: ExistingZones = underTest.getZonesForRequest(List(ptr)).unsafeRunSync() val zoneNames = underTestPTRZonesList.zones.map(_.name) @@ -1198,7 +1202,7 @@ class BatchChangeServiceSpec ) val ip = "2001:0db8:0000:0000:0000:ff00:0042:8329" - val ptr = AddChangeInput(ip, RecordType.PTR, ttl, PTRData(Fqdn("ptr."))).validNel + val ptr = AddChangeInput(ip, RecordType.PTR, None, ttl, PTRData(Fqdn("ptr."))).validNel val underTestPTRZonesList: ExistingZones = underTest.getZonesForRequest(List(ptr)).unsafeRunSync() val zoneNames = underTestPTRZonesList.zones.map(_.name) @@ -1245,7 +1249,7 @@ class BatchChangeServiceSpec val ips = ip1 :: ip2s val ptrs = ips.map { v6Name => - AddChangeInput(v6Name, RecordType.PTR, ttl, PTRData(Fqdn("ptr."))).validNel + AddChangeInput(v6Name, RecordType.PTR, None, ttl, PTRData(Fqdn("ptr."))).validNel } val underTestPTRZonesList: ExistingZones = underTest.getZonesForRequest(ptrs).unsafeRunSync() @@ -1300,10 +1304,10 @@ class BatchChangeServiceSpec "properly discover records in forward zones" in { val apex = apexZone.name - val aApex = AddChangeInput(apex, RecordType.A, ttl, AData("1.2.3.4")) - val aNormal = AddChangeInput(s"record.$apex", RecordType.A, ttl, AData("1.2.3.4")) + val aApex = AddChangeInput(apex, RecordType.A, None, ttl, AData("1.2.3.4")) + val aNormal = AddChangeInput(s"record.$apex", RecordType.A, None, ttl, AData("1.2.3.4")) val aDotted = - AddChangeInput(s"some.dotted.record.$apex", RecordType.A, ttl, AData("1.2.3.4")) + AddChangeInput(s"some.dotted.record.$apex", RecordType.A, None, ttl, AData("1.2.3.4")) val expected = List( AddChangeForValidation(apexZone, apex, aApex, 7200L), @@ -1322,10 +1326,10 @@ class BatchChangeServiceSpec "properly discover TXT records" in { val apex = apexZone.name - val txtApex = AddChangeInput(apex, RecordType.TXT, ttl, TXTData("test")) - val txtNormal = AddChangeInput(s"record.$apex", RecordType.TXT, ttl, TXTData("test")) + val txtApex = AddChangeInput(apex, RecordType.TXT, None, ttl, TXTData("test")) + val txtNormal = AddChangeInput(s"record.$apex", RecordType.TXT, None, ttl, TXTData("test")) val txtDotted = - AddChangeInput(s"some.dotted.record.$apex", RecordType.TXT, ttl, TXTData("test")) + AddChangeInput(s"some.dotted.record.$apex", RecordType.TXT, None, ttl, TXTData("test")) val expected = List( AddChangeForValidation(apexZone, apex, txtApex, 7200L), @@ -1419,20 +1423,22 @@ class BatchChangeServiceSpec val ptrv6ZoneBig = Zone("0.1.0.0.2.ip6.arpa.", "email", id = "ptrv6big") val smallZoneAdd = - AddChangeInput("2001:db8::ff00:42:8329", RecordType.PTR, ttl, PTRData(Fqdn("ptr"))) + AddChangeInput("2001:db8::ff00:42:8329", RecordType.PTR, None, ttl, PTRData(Fqdn("ptr"))) val medZoneAdd = AddChangeInput( "2001:0db8:0111:0000:0000:ff00:0042:8329", RecordType.PTR, + None, ttl, PTRData(Fqdn("ptr")) ) val bigZoneAdd = AddChangeInput( "2001:0000:0000:0000:0000:ff00:0042:8329", RecordType.PTR, + None, ttl, PTRData(Fqdn("ptr")) ) - val notFoundZoneAdd = AddChangeInput("::1", RecordType.PTR, ttl, PTRData(Fqdn("ptr"))) + val notFoundZoneAdd = AddChangeInput("::1", RecordType.PTR, None, ttl, PTRData(Fqdn("ptr"))) val ptripv6Adds = List( smallZoneAdd.validNel, @@ -1678,7 +1684,7 @@ class BatchChangeServiceSpec "return a BatchChange if all data inputs are valid/soft failures and manual review is enabled and owner group ID " + "is provided" in { - val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT) + val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT, None) val result = underTestManualEnabled .buildResponse( BatchChangeInput(None, List(apexAddA, onlyBaseAddAAAA, delete), Some("owner-group-ID")), @@ -1805,7 +1811,7 @@ class BatchChangeServiceSpec } "return a BatchChangeErrorList if all data inputs are valid/soft failures and manual review is disabled" in { - val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT) + val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT, None) val result = underTest .buildResponse( BatchChangeInput(None, List(apexAddA, onlyBaseAddAAAA, delete)), @@ -1825,7 +1831,7 @@ class BatchChangeServiceSpec "return a BatchChangeErrorList if all data inputs are valid/soft failures, scheduled, " + "and manual review is disabled" in { - val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT) + val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT, None) val result = underTest .buildResponse( BatchChangeInput( @@ -1872,7 +1878,7 @@ class BatchChangeServiceSpec "return a BatchChangeErrorList if all data inputs are valid/soft failures, manual review is enabled, " + "but batch change allowManualReview attribute is false" in { - val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT) + val delete = DeleteRRSetChangeInput("some.test.delete.", RecordType.TXT, None) val result = underTestManualEnabled .buildResponse( BatchChangeInput(None, List(apexAddA, onlyBaseAddAAAA, delete)), diff --git a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeValidationsSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeValidationsSpec.scala index aef6bf2f8..bf940d4b0 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeValidationsSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/batch/BatchChangeValidationsSpec.scala @@ -92,12 +92,12 @@ class BatchChangeValidationsSpec private val validAChangeGen: Gen[AddChangeInput] = for { fqdn <- domainGenerator ip <- validIpv4Gen - } yield AddChangeInput(fqdn, RecordType.A, ttl, AData(ip)) + } yield AddChangeInput(fqdn, RecordType.A, None, ttl, AData(ip)) private val validAAAAChangeGen: Gen[AddChangeInput] = for { fqdn <- domainGenerator ip <- validIpv6Gen - } yield AddChangeInput(fqdn, RecordType.AAAA, ttl, AAAAData(ip)) + } yield AddChangeInput(fqdn, RecordType.AAAA, None, ttl, AAAAData(ip)) private val validAddChangeForValidationGen: Gen[AddChangeForValidation] = for { recordName <- domainComponentGenerator @@ -107,7 +107,7 @@ class BatchChangeValidationsSpec private def generateValidAddChangeForValidation(rs: RecordSet): Gen[AddChangeForValidation] = for { recordName <- domainComponentGenerator - addChangeInput <- AddChangeInput(recordName, rs.typ, Some(rs.ttl), rs.records.head) + addChangeInput <- AddChangeInput(recordName, rs.typ, None, Some(rs.ttl), rs.records.head) } yield AddChangeForValidation(validZone, recordName, addChangeInput, defaultTtl) private val recordSetList = List(rsOk, aaaa, aaaaOrigin, abcRecord) @@ -121,59 +121,59 @@ class BatchChangeValidationsSpec private val createPrivateAddChange = AddChangeForValidation( okZone, "private-create", - AddChangeInput("private-create", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("private-create", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) private val createSharedAddChange = AddChangeForValidation( sharedZone, "shared-create", - AddChangeInput("shared-create", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("shared-create", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) private val updatePrivateAddChange = AddChangeForValidation( okZone, "private-update", - AddChangeInput("private-update", RecordType.A, ttl, AAAAData("1.2.3.4")), + AddChangeInput("private-update", RecordType.A, None, ttl, AAAAData("1.2.3.4")), defaultTtl ) private val updatePrivateDeleteChange = DeleteRRSetChangeForValidation( okZone, "private-update", - DeleteRRSetChangeInput("private-update", RecordType.A) + DeleteRRSetChangeInput("private-update", RecordType.A, None) ) private val updateSharedAddChange = AddChangeForValidation( sharedZone, "shared-update", - AddChangeInput("shared-update", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")), + AddChangeInput("shared-update", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")), defaultTtl ) private val updateSharedDeleteChange = DeleteRRSetChangeForValidation( sharedZone, "shared-update", - DeleteRRSetChangeInput("shared-update", RecordType.AAAA) + DeleteRRSetChangeInput("shared-update", RecordType.AAAA, None) ) private val deleteSingleRecordChange = DeleteRRSetChangeForValidation( sharedZone, "shared-update", - DeleteRRSetChangeInput("shared-update", RecordType.AAAA, Some(AAAAData("1:0::1"))) + DeleteRRSetChangeInput("shared-update", RecordType.AAAA, None, Some(AAAAData("1:0::1"))) ) private val deletePrivateChange = DeleteRRSetChangeForValidation( okZone, "private-delete", - DeleteRRSetChangeInput("private-delete", RecordType.A) + DeleteRRSetChangeInput("private-delete", RecordType.A, None) ) private val deleteSharedChange = DeleteRRSetChangeForValidation( sharedZone, "shared-delete", - DeleteRRSetChangeInput("shared-delete", RecordType.AAAA) + DeleteRRSetChangeInput("shared-delete", RecordType.AAAA, None) ) private val validPendingBatchChange = BatchChange( @@ -201,7 +201,7 @@ class BatchChangeValidationsSpec AddChangeForValidation( okZone, s"$recordName", - AddChangeInput(s"$recordName.ok.", RecordType.A, ttl, aData), + AddChangeInput(s"$recordName.ok.", RecordType.A, None, ttl, aData), defaultTtl ) @@ -212,7 +212,7 @@ class BatchChangeValidationsSpec DeleteRRSetChangeForValidation( okZone, s"$recordName", - DeleteRRSetChangeInput(s"$recordName.ok.", RecordType.A, recordData) + DeleteRRSetChangeInput(s"$recordName.ok.", RecordType.A, None, recordData) ) property("validateBatchChangeInputSize: should fail if batch has no changes") { @@ -404,7 +404,7 @@ class BatchChangeValidationsSpec ) { val input = BatchChangeInput( None, - List(AddChangeInput("private-create", RecordType.A, ttl, AData("1.1.1.1"))), + List(AddChangeInput("private-create", RecordType.A, None, ttl, AData("1.1.1.1"))), scheduledTime = Some(Instant.now.truncatedTo(ChronoUnit.MILLIS)) ) val bcv = @@ -426,7 +426,7 @@ class BatchChangeValidationsSpec ) { val input = BatchChangeInput( None, - List(AddChangeInput("private-create", RecordType.A, ttl, AData("1.1.1.1"))), + List(AddChangeInput("private-create", RecordType.A, None, ttl, AData("1.1.1.1"))), scheduledTime = Some(Instant.now.truncatedTo(ChronoUnit.MILLIS).minus(1, ChronoUnit.HOURS)) ) val bcv = @@ -565,21 +565,21 @@ class BatchChangeValidationsSpec } property("validateInputChanges: should fail with mix of success and failure inputs") { - val goodNSInput = AddChangeInput("test-ns.example.com.", RecordType.NS, ttl, NSData(Fqdn("some.test.ns."))) - val goodNAPTRInput = AddChangeInput("test-naptr.example.com.", RecordType.NAPTR, ttl, NAPTRData(1, 2, "S", "E2U+sip", "!^.*$!sip:jd@corpxyz.com!", Fqdn("target"))) - val goodSRVInput = AddChangeInput("test-srv.example.com.", RecordType.SRV, ttl, SRVData(1, 2, 3, Fqdn("target.vinyldns."))) - val badNSInput = AddChangeInput("test-bad-ns.example.com.", RecordType.NS, ttl, NSData(Fqdn("some.te$st.ns."))) - val badNAPTRInput = AddChangeInput("test-bad-naptr.example.com.", RecordType.NAPTR, ttl, NAPTRData(99999, 2, "S", "E2U+sip", "", Fqdn("target"))) - val badNAPTRFlagInput = AddChangeInput("test-bad-flag-naptr.example.com.", RecordType.NAPTR, ttl, NAPTRData(1, 2, "t", "E2U+sip", "", Fqdn("target"))) - val badNAPTRRegexpInput = AddChangeInput("test-bad-regexp-naptr.example.com.", RecordType.NAPTR, ttl, NAPTRData(1, 2, "S", "E2U+sip", "dummyregexp", Fqdn("target"))) - val badSRVInput = AddChangeInput("test-bad-srv.example.com.", RecordType.SRV, ttl, SRVData(99999, 2, 3, Fqdn("target.vinyldns."))) - val goodInput = AddChangeInput("test.example.com.", RecordType.A, ttl, AData("1.1.1.1")) + val goodNSInput = AddChangeInput("test-ns.example.com.", RecordType.NS, None, ttl, NSData(Fqdn("some.test.ns."))) + val goodNAPTRInput = AddChangeInput("test-naptr.example.com.", RecordType.NAPTR, None, ttl, NAPTRData(1, 2, "S", "E2U+sip", "!^.*$!sip:jd@corpxyz.com!", Fqdn("target"))) + val goodSRVInput = AddChangeInput("test-srv.example.com.", RecordType.SRV, None, ttl, SRVData(1, 2, 3, Fqdn("target.vinyldns."))) + val badNSInput = AddChangeInput("test-bad-ns.example.com.", RecordType.NS, None, ttl, NSData(Fqdn("some.te$st.ns."))) + val badNAPTRInput = AddChangeInput("test-bad-naptr.example.com.", RecordType.NAPTR, None, ttl, NAPTRData(99999, 2, "S", "E2U+sip", "", Fqdn("target"))) + val badNAPTRFlagInput = AddChangeInput("test-bad-flag-naptr.example.com.", RecordType.NAPTR, None, ttl, NAPTRData(1, 2, "t", "E2U+sip", "", Fqdn("target"))) + val badNAPTRRegexpInput = AddChangeInput("test-bad-regexp-naptr.example.com.", RecordType.NAPTR, None, ttl, NAPTRData(1, 2, "S", "E2U+sip", "dummyregexp", Fqdn("target"))) + val badSRVInput = AddChangeInput("test-bad-srv.example.com.", RecordType.SRV, None, ttl, SRVData(99999, 2, 3, Fqdn("target.vinyldns."))) + val goodInput = AddChangeInput("test.example.com.", RecordType.A, None, ttl, AData("1.1.1.1")) val goodAAAAInput = - AddChangeInput("testAAAA.example.com.", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")) + AddChangeInput("testAAAA.example.com.", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")) val invalidDomainNameInput = - AddChangeInput("invalidDomainName$", RecordType.A, ttl, AAAAData("1:2:3:4:5:6:7:8")) + AddChangeInput("invalidDomainName$", RecordType.A, None, ttl, AAAAData("1:2:3:4:5:6:7:8")) val invalidIpv6Input = - AddChangeInput("testbad.example.com.", RecordType.AAAA, ttl, AAAAData("invalidIpv6:123")) + AddChangeInput("testbad.example.com.", RecordType.AAAA, None, ttl, AAAAData("invalidIpv6:123")) val result = validateInputChanges( List(goodNSInput, goodNAPTRInput, goodSRVInput, goodInput, goodAAAAInput, invalidDomainNameInput, invalidIpv6Input, badNSInput, badNAPTRInput, badNAPTRFlagInput, badNAPTRRegexpInput, badSRVInput), @@ -601,10 +601,10 @@ class BatchChangeValidationsSpec property("""validateInputName: should fail with a HighValueDomainError |if inputName is a High Value Domain""".stripMargin) { - val changeA = AddChangeInput("high-value-domain.foo.", RecordType.A, ttl, AData("1.1.1.1")) - val changeIpV4 = AddChangeInput("192.0.2.252", RecordType.PTR, ttl, PTRData(Fqdn("test."))) + val changeA = AddChangeInput("high-value-domain.foo.", RecordType.A, None, ttl, AData("1.1.1.1")) + val changeIpV4 = AddChangeInput("192.0.2.252", RecordType.PTR, None, ttl, PTRData(Fqdn("test."))) val changeIpV6 = - AddChangeInput("fd69:27cc:fe91:0:0:0:0:ffff", RecordType.PTR, ttl, PTRData(Fqdn("test."))) + AddChangeInput("fd69:27cc:fe91:0:0:0:0:ffff", RecordType.PTR, None, ttl, PTRData(Fqdn("test."))) val resultA = validateInputName(changeA, false) val resultIpV4 = validateInputName(changeIpV4, false) @@ -621,10 +621,10 @@ class BatchChangeValidationsSpec property("""validateInputName: should fail with a RecordRequiresManualReview |if inputName is matches domain requiring manual review""".stripMargin) { - val changeA = AddChangeInput("needs-review.foo.", RecordType.A, ttl, AData("1.1.1.1")) - val changeIpV4 = AddChangeInput("192.0.2.254", RecordType.PTR, ttl, PTRData(Fqdn("test."))) + val changeA = AddChangeInput("needs-review.foo.", RecordType.A, None, ttl, AData("1.1.1.1")) + val changeIpV4 = AddChangeInput("192.0.2.254", RecordType.PTR, None, ttl, PTRData(Fqdn("test."))) val changeIpV6 = - AddChangeInput("fd69:27cc:fe91:0:0:0:ffff:1", RecordType.PTR, ttl, PTRData(Fqdn("test."))) + AddChangeInput("fd69:27cc:fe91:0:0:0:ffff:1", RecordType.PTR, None, ttl, PTRData(Fqdn("test."))) val resultA = validateInputName(changeA, false) val resultIpV4 = validateInputName(changeIpV4, false) @@ -640,14 +640,14 @@ class BatchChangeValidationsSpec } property("doesNotRequireManualReview: should succeed if user is reviewing") { - val changeA = AddChangeInput("needs-review.foo.", RecordType.A, ttl, AData("1.1.1.1")) + val changeA = AddChangeInput("needs-review.foo.", RecordType.A, None, ttl, AData("1.1.1.1")) validateInputName(changeA, true) should beValid(()) } property("""zoneDoesNotRequireManualReview: should fail with RecordRequiresManualReview |if zone name matches domain requiring manual review""".stripMargin) { val addChangeInput = - AddChangeInput("not-allowed.zone.NEEDS.review", RecordType.A, ttl, AData("1.1.1.1")) + AddChangeInput("not-allowed.zone.NEEDS.review", RecordType.A, None, ttl, AData("1.1.1.1")) val addChangeForValidation = AddChangeForValidation( Zone("Zone.needs.review", "some@email.com"), "not-allowed", @@ -662,7 +662,7 @@ class BatchChangeValidationsSpec property("""zoneDoesNotRequireManualReview: should succeed if user is reviewing""") { val addChangeInput = - AddChangeInput("not-allowed.zone.NEEDS.review", RecordType.A, ttl, AData("1.1.1.1")) + AddChangeInput("not-allowed.zone.NEEDS.review", RecordType.A, None, ttl, AData("1.1.1.1")) val addChangeForValidation = AddChangeForValidation( Zone("Zone.needs.review", "some@email.com"), "not-allowed", @@ -674,7 +674,7 @@ class BatchChangeValidationsSpec property("""validateInputName: should fail with a DomainValidationError for deletes |if validateHostName fails for an invalid domain name""".stripMargin) { - val change = DeleteRRSetChangeInput("invalidDomainName$", RecordType.A) + val change = DeleteRRSetChangeInput("invalidDomainName$", RecordType.A, None) val result = validateInputName(change, false) result should haveInvalid[DomainValidationError](InvalidDomainName("invalidDomainName$.")) } @@ -682,7 +682,7 @@ class BatchChangeValidationsSpec property("""validateInputName: should fail with a DomainValidationError for deletes |if validateHostName fails for an invalid domain name length""".stripMargin) { val invalidDomainName = Random.alphanumeric.take(256).mkString - val change = DeleteRRSetChangeInput(invalidDomainName, RecordType.AAAA) + val change = DeleteRRSetChangeInput(invalidDomainName, RecordType.AAAA, None) val result = validateInputName(change, false) result should haveInvalid[DomainValidationError](InvalidDomainName(s"$invalidDomainName.")) .and(haveInvalid[DomainValidationError](InvalidLength(s"$invalidDomainName.", 2, 255))) @@ -691,7 +691,7 @@ class BatchChangeValidationsSpec property("""validateInputName: PTR should fail with InvalidIPAddress for deletes |if inputName is not a valid ipv4 or ipv6 address""".stripMargin) { val invalidIp = "invalidIp.111" - val change = DeleteRRSetChangeInput(invalidIp, RecordType.PTR) + val change = DeleteRRSetChangeInput(invalidIp, RecordType.PTR, None) val result = validateInputName(change, false) result should haveInvalid[DomainValidationError](InvalidIPAddress(invalidIp)) } @@ -714,7 +714,7 @@ class BatchChangeValidationsSpec property("""validateAddChangeInput: should fail with a DomainValidationError |if validateHostName fails for an invalid domain name""".stripMargin) { - val change = AddChangeInput("invalidDomainName$", RecordType.A, ttl, AData("1.1.1.1")) + val change = AddChangeInput("invalidDomainName$", RecordType.A, None, ttl, AData("1.1.1.1")) val result = validateAddChangeInput(change, false) result should haveInvalid[DomainValidationError](InvalidDomainName("invalidDomainName$.")) } @@ -722,7 +722,7 @@ class BatchChangeValidationsSpec property("""validateAddChangeInput: should fail with a DomainValidationError |if validateHostName fails for an invalid domain name length""".stripMargin) { val invalidDomainName = Random.alphanumeric.take(256).mkString - val change = AddChangeInput(invalidDomainName, RecordType.A, ttl, AData("1.1.1.1")) + val change = AddChangeInput(invalidDomainName, RecordType.A, None, ttl, AData("1.1.1.1")) val result = validateAddChangeInput(change, false) result should haveInvalid[DomainValidationError](InvalidDomainName(s"$invalidDomainName.")) .and(haveInvalid[DomainValidationError](InvalidLength(s"$invalidDomainName.", 2, 255))) @@ -733,7 +733,7 @@ class BatchChangeValidationsSpec ) { forAll(choose[Long](0, 29)) { invalidTTL: Long => val change = - AddChangeInput("test.comcast.com.", RecordType.A, Some(invalidTTL), AData("1.1.1.1")) + AddChangeInput("test.comcast.com.", RecordType.A, None, Some(invalidTTL), AData("1.1.1.1")) val result = validateAddChangeInput(change, false) result should haveInvalid[DomainValidationError]( InvalidTTL(invalidTTL, DomainValidations.TTL_MIN_LENGTH, DomainValidations.TTL_MAX_LENGTH) @@ -744,7 +744,7 @@ class BatchChangeValidationsSpec property("""validateAddChangeInput: should fail with InvalidIpv4Address |if validateRecordData fails for an invalid ipv4 address""".stripMargin) { val invalidIpv4 = "invalidIpv4:123" - val change = AddChangeInput("test.comcast.com.", RecordType.A, ttl, AData(invalidIpv4)) + val change = AddChangeInput("test.comcast.com.", RecordType.A, None, ttl, AData(invalidIpv4)) val result = validateAddChangeInput(change, false) result should haveInvalid[DomainValidationError](InvalidIpv4Address(invalidIpv4)) } @@ -752,14 +752,14 @@ class BatchChangeValidationsSpec property("""validateAddChangeInput: should fail with InvalidIpv6Address |if validateRecordData fails for an invalid ipv6 address""".stripMargin) { val invalidIpv6 = "invalidIpv6:123" - val change = AddChangeInput("test.comcast.com.", RecordType.AAAA, ttl, AAAAData(invalidIpv6)) + val change = AddChangeInput("test.comcast.com.", RecordType.AAAA, None, ttl, AAAAData(invalidIpv6)) val result = validateAddChangeInput(change, false) result should haveInvalid[DomainValidationError](InvalidIpv6Address(invalidIpv6)) } property("validateAddChangeInput: should fail if A inputName includes a reverse zone address") { val invalidInputName = "test.1.2.3.in-addr.arpa." - val badAChange = AddChangeInput(invalidInputName, RecordType.A, ttl, AData("1.1.1.1")) + val badAChange = AddChangeInput(invalidInputName, RecordType.A, None, ttl, AData("1.1.1.1")) val result = validateAddChangeInput(badAChange, false) result should haveInvalid[DomainValidationError]( RecordInReverseZoneError(invalidInputName, RecordType.A.toString) @@ -769,7 +769,7 @@ class BatchChangeValidationsSpec property("validateAddChangeInput: should fail if AAAA inputName includes a reverse zone address") { val invalidInputName = "test.1.2.3.ip6.arpa." val badAAAAChange = - AddChangeInput(invalidInputName, RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")) + AddChangeInput(invalidInputName, RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")) val result = validateAddChangeInput(badAAAAChange, false) result should haveInvalid[DomainValidationError]( RecordInReverseZoneError(invalidInputName, RecordType.AAAA.toString) @@ -783,6 +783,7 @@ class BatchChangeValidationsSpec AddChangeInput( "test.comcast.com.", RecordType.CNAME, + None, ttl, CNAMEData(Fqdn(invalidCNAMERecordData)) ) @@ -798,6 +799,7 @@ class BatchChangeValidationsSpec AddChangeInput( "test.comcast.com.", RecordType.CNAME, + None, ttl, CNAMEData(Fqdn(invalidCNAMERecordData)) ) @@ -813,6 +815,7 @@ class BatchChangeValidationsSpec AddChangeInput( "test.comcast.com.", RecordType.CNAME, + None, ttl, CNAMEData(Fqdn(invalidCNAMERecordData)) ) @@ -826,7 +829,7 @@ class BatchChangeValidationsSpec property("""validateAddChangeInput: PTR should fail with InvalidIPAddress |if inputName is not a valid ipv4 or ipv6 address""".stripMargin) { val invalidIp = "invalidip.111." - val change = AddChangeInput(invalidIp, RecordType.PTR, ttl, PTRData(Fqdn("test.comcast.com"))) + val change = AddChangeInput(invalidIp, RecordType.PTR, None, ttl, PTRData(Fqdn("test.comcast.com"))) val result = validateAddChangeInput(change, false) result should haveInvalid[DomainValidationError](InvalidIPAddress(invalidIp)) @@ -834,7 +837,7 @@ class BatchChangeValidationsSpec property("validateAddChangeInput: should fail with InvalidDomainName for invalid PTR record data") { val invalidPTRDname = "*invalidptrdname" - val change = AddChangeInput("4.5.6.7", RecordType.PTR, ttl, PTRData(Fqdn(invalidPTRDname))) + val change = AddChangeInput("4.5.6.7", RecordType.PTR, None, ttl, PTRData(Fqdn(invalidPTRDname))) val result = validateAddChangeInput(change, false) result should haveInvalid[DomainValidationError](InvalidDomainName(s"$invalidPTRDname.")) @@ -848,43 +851,43 @@ class BatchChangeValidationsSpec val addNsRecord = AddChangeForValidation( okZone, "ns-add", - AddChangeInput("ns-add.ok.", RecordType.NS, ttl, NSData(Fqdn("some.test.ns."))), + AddChangeInput("ns-add.ok.", RecordType.NS, None, ttl, NSData(Fqdn("some.test.ns."))), defaultTtl ) val addNaptrRecord = AddChangeForValidation( okZone, "naptr-add", - AddChangeInput("naptr-add.ok.", RecordType.NAPTR, ttl, NAPTRData(1, 2, "S", "E2U+sip", "", Fqdn("target"))), + AddChangeInput("naptr-add.ok.", RecordType.NAPTR, None, ttl, NAPTRData(1, 2, "S", "E2U+sip", "", Fqdn("target"))), defaultTtl ) val addSrvRecord = AddChangeForValidation( okZone, "srv-add", - AddChangeInput("srv-add.ok.", RecordType.SRV, ttl, SRVData(1, 2, 3, Fqdn("target.vinyldns."))), + AddChangeInput("srv-add.ok.", RecordType.SRV, None, ttl, SRVData(1, 2, 3, Fqdn("target.vinyldns."))), defaultTtl ) val addA1 = AddChangeForValidation( authZone, "valid", - AddChangeInput("valid.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("valid.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val existingA = AddChangeForValidation( authZone, "existingA", - AddChangeInput("existingA.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("existingA.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val existingCname = AddChangeForValidation( authZone, "existingCname", - AddChangeInput("existingCname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname"))), + AddChangeInput("existingCname.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("cname"))), defaultTtl ) val addA2 = AddChangeForValidation( okZone, "valid2", - AddChangeInput("valid2.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("valid2.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val duplicateNameCname = AddChangeForValidation( @@ -893,6 +896,7 @@ class BatchChangeValidationsSpec AddChangeInput( "199.2.0.192.in-addr.arpa.", RecordType.CNAME, + None, ttl, CNAMEData(Fqdn("199.192/30.2.0.192.in-addr.arpa")) ), @@ -901,7 +905,7 @@ class BatchChangeValidationsSpec val duplicateNamePTR = AddChangeForValidation( reverseZone, "199", - AddChangeInput("192.0.2.199", RecordType.PTR, ttl, PTRData(Fqdn("ptr.ok."))), + AddChangeInput("192.0.2.199", RecordType.PTR, None, ttl, PTRData(Fqdn("ptr.ok."))), defaultTtl ) @@ -1051,13 +1055,13 @@ class BatchChangeValidationsSpec val addUpdateA = AddChangeForValidation( okZone.addACLRule(writeAcl), "update", - AddChangeInput("update.ok.", RecordType.A, ttl, AData("1.2.3.4")), + AddChangeInput("update.ok.", RecordType.A, None, ttl, AData("1.2.3.4")), defaultTtl ) val deleteUpdateA = DeleteRRSetChangeForValidation( okZone.addACLRule(writeAcl), "update", - DeleteRRSetChangeInput("update.ok.", RecordType.A) + DeleteRRSetChangeInput("update.ok.", RecordType.A, None) ) val result = validateChangesWithContext( ChangeForValidationMap( @@ -1082,13 +1086,13 @@ class BatchChangeValidationsSpec val addUpdateA = AddChangeForValidation( okZone.addACLRule(readAcl), "update", - AddChangeInput("update.ok.", RecordType.A, ttl, AData("1.2.3.4")), + AddChangeInput("update.ok.", RecordType.A, None, ttl, AData("1.2.3.4")), defaultTtl ) val deleteUpdateA = DeleteRRSetChangeForValidation( okZone.addACLRule(readAcl), "update", - DeleteRRSetChangeInput("update.ok.", RecordType.A) + DeleteRRSetChangeInput("update.ok.", RecordType.A, None) ) val result = validateChangesWithContext( ChangeForValidationMap( @@ -1183,14 +1187,14 @@ class BatchChangeValidationsSpec val addUpdateA = AddChangeForValidation( sharedZone, "mx", - AddChangeInput("mx.shared.", RecordType.MX, ttl, MXData(200, Fqdn("mx"))), + AddChangeInput("mx.shared.", RecordType.MX, None, ttl, MXData(200, Fqdn("mx"))), defaultTtl ) val deleteUpdateA = DeleteRRSetChangeForValidation( sharedZone, "mx", - DeleteRRSetChangeInput("mx.shared.", RecordType.MX) + DeleteRRSetChangeInput("mx.shared.", RecordType.MX, None) ) val result = validateChangesWithContext( ChangeForValidationMap( @@ -1214,7 +1218,7 @@ class BatchChangeValidationsSpec val deleteCnameRRSet = DeleteRRSetChangeForValidation( okZone, "deleteRRSet", - DeleteRRSetChangeInput("deleteRRSet.ok.", RecordType.CNAME) + DeleteRRSetChangeInput("deleteRRSet.ok.", RecordType.CNAME, None) ) val deleteCnameEntry = DeleteRRSetChangeForValidation( okZone, @@ -1222,6 +1226,7 @@ class BatchChangeValidationsSpec DeleteRRSetChangeInput( "deleteRecord.ok.", RecordType.CNAME, + None, Some(CNAMEData(Fqdn("cname.data."))) ) ) @@ -1327,13 +1332,13 @@ class BatchChangeValidationsSpec val addCname = AddChangeForValidation( validZone, "existingCname", - AddChangeInput("existingCname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname"))), + AddChangeInput("existingCname.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("cname"))), defaultTtl ) val deleteA = DeleteRRSetChangeForValidation( validZone, "existingCname", - DeleteRRSetChangeInput("existingCname.ok.", RecordType.A) + DeleteRRSetChangeInput("existingCname.ok.", RecordType.A, None) ) val existingA = rsOk.copy(zoneId = addCname.zone.id, name = addCname.recordName) val newRecordSetList = existingA :: recordSetList @@ -1356,7 +1361,7 @@ class BatchChangeValidationsSpec val addCname = AddChangeForValidation( validZone, "existingCname", - AddChangeInput("existingCname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname"))), + AddChangeInput("existingCname.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("cname"))), defaultTtl ) val existingA = rsOk.copy(zoneId = addCname.zone.id, name = addCname.recordName) @@ -1378,13 +1383,13 @@ class BatchChangeValidationsSpec val addCname = AddChangeForValidation( validIp4ReverseZone, "30", - AddChangeInput("30.2.0.192.in-addr.arpa.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname"))), + AddChangeInput("30.2.0.192.in-addr.arpa.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("cname"))), defaultTtl ) val deletePtr = DeleteRRSetChangeForValidation( validIp4ReverseZone, "30", - DeleteRRSetChangeInput("192.0.2.30", RecordType.PTR) + DeleteRRSetChangeInput("192.0.2.30", RecordType.PTR, None) ) val ptr4 = ptrIp4.copy(zoneId = validIp4ReverseZone.id) val result = validateChangesWithContext( @@ -1409,6 +1414,7 @@ class BatchChangeValidationsSpec AddChangeInput( "0.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.9.e.f.c.c.7.2.9.6.d.f.ip6.arpa.", RecordType.CNAME, + None, ttl, CNAMEData(Fqdn("cname")) ), @@ -1432,19 +1438,19 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( okZone, "test", - AddChangeInput("test.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("test.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val addAAAA = AddChangeForValidation( okZone, "testAAAA", - AddChangeInput("testAAAA.ok.", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")), + AddChangeInput("testAAAA.ok.", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")), defaultTtl ) val addCname = AddChangeForValidation( okZone, "new", - AddChangeInput("new.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("hey.ok.com."))), + AddChangeInput("new.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("hey.ok.com."))), defaultTtl ) val result = validateChangesWithContext( @@ -1467,19 +1473,19 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( okZone, "test", - AddChangeInput("test.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("test.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val addDuplicateCname = AddChangeForValidation( okZone, "testAAAA", - AddChangeInput("testAAAA.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("hey.ok.com."))), + AddChangeInput("testAAAA.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("hey.ok.com."))), defaultTtl ) val addAAAA = AddChangeForValidation( okZone, "testAAAA", - AddChangeInput("testAAAA.ok.", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")), + AddChangeInput("testAAAA.ok.", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")), defaultTtl ) val result = validateChangesWithContext( @@ -1507,19 +1513,19 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( okZone, "test", - AddChangeInput("test.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("test.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val addCname = AddChangeForValidation( okZone, "testAAAA", - AddChangeInput("testAAAA.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("hey.ok.com."))), + AddChangeInput("testAAAA.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("hey.ok.com."))), defaultTtl ) val addDuplicateCname = AddChangeForValidation( okZone, "testAAAA", - AddChangeInput("testAAAA.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("hey2.ok.com."))), + AddChangeInput("testAAAA.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("hey2.ok.com."))), defaultTtl ) val result = validateChangesWithContext( @@ -1549,19 +1555,19 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( okZone, "test", - AddChangeInput("test.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("test.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val addPtr = AddChangeForValidation( okZone, "193", - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("test.ok."))), + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("test.ok."))), defaultTtl ) val addDuplicatePtr = AddChangeForValidation( okZone, "193", - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("hey.ok.com."))), + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("hey.ok.com."))), defaultTtl ) val result = validateChangesWithContext( @@ -1582,7 +1588,7 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( validZone, "valid", - AddChangeInput("valid.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("valid.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val result = @@ -1602,7 +1608,7 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( validZone, "valid", - AddChangeInput("valid.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("valid.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val result = validateChangesWithContext( @@ -1628,7 +1634,7 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( validZone.addACLRule(ACLRule(accessLevel = AccessLevel.Write, userId = Some(notAuth.userId))), "valid", - AddChangeInput("valid.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("valid.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val result = @@ -1671,13 +1677,13 @@ class BatchChangeValidationsSpec val addCname = AddChangeForValidation( validZone, "existing", - AddChangeInput("existing.ok.", RecordType.CNAME, ttl, PTRData(Fqdn("orders.vinyldns."))), + AddChangeInput("existing.ok.", RecordType.CNAME, None, ttl, PTRData(Fqdn("orders.vinyldns."))), defaultTtl ) val addPtr = AddChangeForValidation( validZone, "existing", - AddChangeInput("existing.ok.", RecordType.PTR, ttl, CNAMEData(Fqdn("ptrdname."))), + AddChangeInput("existing.ok.", RecordType.PTR, None, ttl, CNAMEData(Fqdn("ptrdname."))), defaultTtl ) val result = validateChangesWithContext( @@ -1698,7 +1704,7 @@ class BatchChangeValidationsSpec val deleteA = DeleteRRSetChangeForValidation( validZone, "Record-exists", - DeleteRRSetChangeInput("record-exists.ok.", RecordType.A) + DeleteRRSetChangeInput("record-exists.ok.", RecordType.A, None) ) val existingDeleteRecord = rsOk.copy(zoneId = deleteA.zone.id, name = deleteA.recordName.toLowerCase) @@ -1742,7 +1748,7 @@ class BatchChangeValidationsSpec val deleteA = DeleteRRSetChangeForValidation( validZone, "Active-record-status", - DeleteRRSetChangeInput("active-record-status", RecordType.A) + DeleteRRSetChangeInput("active-record-status", RecordType.A, None) ) val existingDeleteRecord = rsOk.copy( zoneId = deleteA.zone.id, @@ -1768,7 +1774,7 @@ class BatchChangeValidationsSpec DeleteRRSetChangeForValidation( validZone, "valid", - DeleteRRSetChangeInput("valid.ok.", RecordType.A) + DeleteRRSetChangeInput("valid.ok.", RecordType.A, None) ) val existingDeleteRecord = rsOk.copy(zoneId = deleteA.zone.id, name = deleteA.recordName) val result = validateChangesWithContext( @@ -1790,7 +1796,7 @@ class BatchChangeValidationsSpec DeleteRRSetChangeForValidation( validZone, "valid", - DeleteRRSetChangeInput("valid.ok.", RecordType.A) + DeleteRRSetChangeInput("valid.ok.", RecordType.A, None) ) val existingDeleteRecord = rsOk.copy(zoneId = deleteA.zone.id, name = deleteA.recordName) val result = validateChangesWithContext( @@ -1821,7 +1827,7 @@ class BatchChangeValidationsSpec ACLRule(accessLevel = AccessLevel.Delete, userId = Some(notAuth.userId)) ), "valid", - DeleteRRSetChangeInput("valid.ok.", RecordType.A) + DeleteRRSetChangeInput("valid.ok.", RecordType.A, None) ) val existingDeleteRecord = rsOk.copy(zoneId = deleteA.zone.id, name = deleteA.recordName) val result = validateChangesWithContext( @@ -1844,7 +1850,7 @@ class BatchChangeValidationsSpec val deleteA = DeleteRRSetChangeForValidation( validZone.addACLRule(ACLRule(accessLevel = AccessLevel.Write, userId = Some(notAuth.userId))), "valid", - DeleteRRSetChangeInput("valid.ok.", RecordType.A) + DeleteRRSetChangeInput("valid.ok.", RecordType.A, None) ) val existingDeleteRecord = rsOk.copy(zoneId = deleteA.zone.id, name = deleteA.recordName) val result = validateChangesWithContext( @@ -1872,13 +1878,13 @@ class BatchChangeValidationsSpec val addDuplicateA = AddChangeForValidation( okZone, "test", - AddChangeInput("test.com.", RecordType.A, ttl, AData("10.1.1.1")), + AddChangeInput("test.com.", RecordType.A, None, ttl, AData("10.1.1.1")), defaultTtl ) val addDuplicateCname = AddChangeForValidation( okZone, "test", - AddChangeInput("test.com.", RecordType.CNAME, ttl, CNAMEData(Fqdn("thing.com."))), + AddChangeInput("test.com.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("thing.com."))), defaultTtl ) @@ -1886,24 +1892,24 @@ class BatchChangeValidationsSpec DeleteRRSetChangeForValidation( okZone, "delete", - DeleteRRSetChangeInput("delete.ok.", RecordType.A) + DeleteRRSetChangeInput("delete.ok.", RecordType.A, None) ) val addCname = AddChangeForValidation( okZone, "delete", - AddChangeInput("delete.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("thing.com."))), + AddChangeInput("delete.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("thing.com."))), defaultTtl ) val addA = AddChangeForValidation( okZone, "delete-this", - AddChangeInput("delete-this.ok.", RecordType.A, ttl, AData("10.1.1.1")), + AddChangeInput("delete-this.ok.", RecordType.A, None, ttl, AData("10.1.1.1")), defaultTtl ) val deleteCname = DeleteRRSetChangeForValidation( okZone, "delete", - DeleteRRSetChangeInput("delete-this.ok.", RecordType.CNAME) + DeleteRRSetChangeInput("delete-this.ok.", RecordType.CNAME, None) ) val existingA = rsOk.copy(zoneId = deleteA.zone.id, name = deleteA.recordName) val existingCname = @@ -1939,29 +1945,29 @@ class BatchChangeValidationsSpec val existingA = rsOk.copy(name = "new") val deleteA = - DeleteRRSetChangeForValidation(okZone, "new", DeleteRRSetChangeInput("new.ok.", RecordType.A)) + DeleteRRSetChangeForValidation(okZone, "new", DeleteRRSetChangeInput("new.ok.", RecordType.A, None)) val addA = AddChangeForValidation( okZone, "test", - AddChangeInput("test.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("test.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val addAAAA = AddChangeForValidation( okZone, "testAAAA", - AddChangeInput("testAAAA.ok.", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")), + AddChangeInput("testAAAA.ok.", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")), defaultTtl ) val addCname = AddChangeForValidation( okZone, "new", - AddChangeInput("new.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("hey.ok."))), + AddChangeInput("new.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("hey.ok."))), defaultTtl ) val addPtr = AddChangeForValidation( okZone, "193", - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("test.ok."))), + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("test.ok."))), defaultTtl ) val result = validateChangesWithContext( @@ -1986,24 +1992,24 @@ class BatchChangeValidationsSpec DeleteRRSetChangeForValidation( okZone, "new", - DeleteRRSetChangeInput("new.ok.", RecordType.CNAME) + DeleteRRSetChangeInput("new.ok.", RecordType.CNAME, None) ) val addA = AddChangeForValidation( okZone, "test", - AddChangeInput("test.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("test.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val addAAAA = AddChangeForValidation( okZone, "new", - AddChangeInput("new.ok.", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")), + AddChangeInput("new.ok.", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")), defaultTtl ) val addPtr = AddChangeForValidation( okZone, "193", - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("test.ok."))), + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("test.ok."))), defaultTtl ) @@ -2029,12 +2035,12 @@ class BatchChangeValidationsSpec DeleteRRSetChangeForValidation( okZone, "new", - DeleteRRSetChangeInput("new.ok.", RecordType.CNAME) + DeleteRRSetChangeInput("new.ok.", RecordType.CNAME, None) ) val addCname = AddChangeForValidation( okZone, "new", - AddChangeInput("new.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("updateData.com"))), + AddChangeInput("new.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("updateData.com"))), defaultTtl ) val result = validateChangesWithContext( @@ -2060,18 +2066,18 @@ class BatchChangeValidationsSpec val deleteUpdateCname = DeleteRRSetChangeForValidation( okZone, "name-conflict", - DeleteRRSetChangeInput("existing.ok.", RecordType.CNAME) + DeleteRRSetChangeInput("existing.ok.", RecordType.CNAME, None) ) val addUpdateCname = AddChangeForValidation( okZone, "name-conflict", - AddChangeInput("add.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("updated.cname."))), + AddChangeInput("add.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("updated.cname."))), defaultTtl ) val addCname = AddChangeForValidation( okZone, "name-conflict", - AddChangeInput("add.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("new.add.cname."))), + AddChangeInput("add.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("new.add.cname."))), defaultTtl ) @@ -2105,12 +2111,12 @@ class BatchChangeValidationsSpec val deletePtr = DeleteRRSetChangeForValidation( validIp4ReverseZone, "193", - DeleteRRSetChangeInput("192.0.2.193", RecordType.PTR) + DeleteRRSetChangeInput("192.0.2.193", RecordType.PTR, None) ) val addCname = AddChangeForValidation( validIp4ReverseZone, "193", - AddChangeInput("test.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("hey2.there."))), + AddChangeInput("test.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("hey2.there."))), defaultTtl ) @@ -2139,12 +2145,12 @@ class BatchChangeValidationsSpec val deletePtr = DeleteRRSetChangeForValidation( validIp4ReverseZone, "193", - DeleteRRSetChangeInput("192.0.2.193", RecordType.PTR) + DeleteRRSetChangeInput("192.0.2.193", RecordType.PTR, None) ) val addPtr = AddChangeForValidation( validIp4ReverseZone, "193", - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("updateData.com"))), + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("updateData.com"))), defaultTtl ) val result = validateChangesWithContext( @@ -2170,18 +2176,18 @@ class BatchChangeValidationsSpec val deleteUpdatePtr = DeleteRRSetChangeForValidation( validIp4ReverseZone, "193", - DeleteRRSetChangeInput("192.0.2.193", RecordType.PTR) + DeleteRRSetChangeInput("192.0.2.193", RecordType.PTR, None) ) val addUpdatePtr = AddChangeForValidation( validIp4ReverseZone, "193", - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("updated.ptr."))), + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("updated.ptr."))), defaultTtl ) val addPtr = AddChangeForValidation( validIp4ReverseZone, "193", - AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData(Fqdn("new.add.ptr."))), + AddChangeInput("192.0.2.193", RecordType.PTR, None, ttl, PTRData(Fqdn("new.add.ptr."))), defaultTtl ) @@ -2199,13 +2205,13 @@ class BatchChangeValidationsSpec } property("validateAddChangeInput: should succeed for a valid TXT addChangeInput") { - val input = AddChangeInput("txt.ok.", RecordType.TXT, ttl, TXTData("test")) + val input = AddChangeInput("txt.ok.", RecordType.TXT, None, ttl, TXTData("test")) val result = validateAddChangeInput(input, false) result shouldBe valid } property("validateAddChangeInput: should fail for a TXT addChangeInput with empty TXTData") { - val input = AddChangeInput("txt.ok.", RecordType.TXT, ttl, TXTData("")) + val input = AddChangeInput("txt.ok.", RecordType.TXT, None, ttl, TXTData("")) val result = validateAddChangeInput(input, false) result should haveInvalid[DomainValidationError](InvalidLength("", 1, 64764)) } @@ -2214,20 +2220,20 @@ class BatchChangeValidationsSpec "validateAddChangeInput: should fail for a TXT addChangeInput with TXTData that is too many characters" ) { val txtData = "x" * 64765 - val input = AddChangeInput("txt.ok.", RecordType.TXT, ttl, TXTData(txtData)) + val input = AddChangeInput("txt.ok.", RecordType.TXT, None, ttl, TXTData(txtData)) val result = validateAddChangeInput(input, false) result should haveInvalid[DomainValidationError](InvalidLength(txtData, 1, 64764)) } property("validateAddChangeInput: should succeed for a valid MX addChangeInput") { - val input = AddChangeInput("mx.ok.", RecordType.MX, ttl, MXData(1, Fqdn("foo.bar."))) + val input = AddChangeInput("mx.ok.", RecordType.MX, None, ttl, MXData(1, Fqdn("foo.bar."))) val result = validateAddChangeInput(input, false) result shouldBe valid } property("validateAddChangeInput: should fail for a MX addChangeInput with invalid preference") { - val inputSmall = AddChangeInput("mx.ok.", RecordType.MX, ttl, MXData(-1, Fqdn("foo.bar."))) - val inputLarge = AddChangeInput("mx.ok.", RecordType.MX, ttl, MXData(1000000, Fqdn("foo.bar."))) + val inputSmall = AddChangeInput("mx.ok.", RecordType.MX, None, ttl, MXData(-1, Fqdn("foo.bar."))) + val inputLarge = AddChangeInput("mx.ok.", RecordType.MX, None, ttl, MXData(1000000, Fqdn("foo.bar."))) val resultSmall = validateAddChangeInput(inputSmall, false) val resultLarge = validateAddChangeInput(inputLarge, false) @@ -2252,7 +2258,7 @@ class BatchChangeValidationsSpec } property("validateAddChangeInput: should fail for a MX addChangeInput with invalid exchange") { - val input = AddChangeInput("mx.ok.", RecordType.MX, ttl, MXData(1, Fqdn("foo$.bar."))) + val input = AddChangeInput("mx.ok.", RecordType.MX, None, ttl, MXData(1, Fqdn("foo$.bar."))) val result = validateAddChangeInput(input, false) result should haveInvalid[DomainValidationError](InvalidDomainName("foo$.bar.")) } @@ -2260,7 +2266,7 @@ class BatchChangeValidationsSpec property( "validateAddChangeInput: should fail for a MX addChangeInput with invalid preference and exchange" ) { - val input = AddChangeInput("mx.ok.", RecordType.MX, ttl, MXData(-1, Fqdn("foo$.bar."))) + val input = AddChangeInput("mx.ok.", RecordType.MX, None, ttl, MXData(-1, Fqdn("foo$.bar."))) val result = validateAddChangeInput(input, false) result should haveInvalid[DomainValidationError]( InvalidMX_NAPTR_SRVData( @@ -2282,13 +2288,13 @@ class BatchChangeValidationsSpec } property("validateDeleteChangeInput: should succeed for valid data when record data is passed in") { - val input = DeleteRRSetChangeInput("a.ok.", RecordType.A, Some(AData("1.1.1.1"))) + val input = DeleteRRSetChangeInput("a.ok.", RecordType.A, None, Some(AData("1.1.1.1"))) validateDeleteRRSetChangeInput(input, false) shouldBe valid } property("validateDeleteChangeInput: should fail when invalid record data is passed in") { val invalidIp = "invalid IP address" - val input = DeleteRRSetChangeInput("a.ok.", RecordType.A, Some(AData(invalidIp))) + val input = DeleteRRSetChangeInput("a.ok.", RecordType.A, None, Some(AData(invalidIp))) val result = validateDeleteRRSetChangeInput(input, false) result should haveInvalid[DomainValidationError](InvalidIpv4Address(invalidIp)) } @@ -2303,7 +2309,7 @@ class BatchChangeValidationsSpec val addMX = AddChangeForValidation( okZone, "name-conflict", - AddChangeInput("name-conflict", RecordType.MX, ttl, MXData(1, Fqdn("foo.bar."))), + AddChangeInput("name-conflict", RecordType.MX, None, ttl, MXData(1, Fqdn("foo.bar."))), defaultTtl ) @@ -2321,13 +2327,13 @@ class BatchChangeValidationsSpec val addMx = AddChangeForValidation( okZone, "name-conflict", - AddChangeInput("name-conflict", RecordType.MX, ttl, MXData(1, Fqdn("foo.bar."))), + AddChangeInput("name-conflict", RecordType.MX, None, ttl, MXData(1, Fqdn("foo.bar."))), defaultTtl ) val addMx2 = AddChangeForValidation( okZone, "name-conflict", - AddChangeInput("name-conflict", RecordType.MX, ttl, MXData(2, Fqdn("foo.bar."))), + AddChangeInput("name-conflict", RecordType.MX, None, ttl, MXData(2, Fqdn("foo.bar."))), defaultTtl ) @@ -2350,12 +2356,12 @@ class BatchChangeValidationsSpec val deleteMx = DeleteRRSetChangeForValidation( okZone, "name-conflict", - DeleteRRSetChangeInput("name-conflict", RecordType.MX) + DeleteRRSetChangeInput("name-conflict", RecordType.MX, None) ) val addMx = AddChangeForValidation( okZone, "name-conflict", - AddChangeInput("name-conflict", RecordType.MX, ttl, MXData(1, Fqdn("foo.bar."))), + AddChangeInput("name-conflict", RecordType.MX, None, ttl, MXData(1, Fqdn("foo.bar."))), defaultTtl ) @@ -2545,16 +2551,16 @@ class BatchChangeValidationsSpec val update1 = updateSharedAddChange.copy( inputChange = - AddChangeInput("shared-update.shared", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")) + AddChangeInput("shared-update.shared", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")) ) val update2 = updateSharedAddChange.copy( - inputChange = AddChangeInput("shared-update.shared", RecordType.AAAA, ttl, AAAAData("1::1")) + inputChange = AddChangeInput("shared-update.shared", RecordType.AAAA, None, ttl, AAAAData("1::1")) ) val add1 = createSharedAddChange.copy( - inputChange = AddChangeInput("shared-add.shared", RecordType.A, ttl, AData("1.2.3.4")) + inputChange = AddChangeInput("shared-add.shared", RecordType.A, None, ttl, AData("1.2.3.4")) ) val add2 = createSharedAddChange.copy( - inputChange = AddChangeInput("shared-add.shared", RecordType.A, ttl, AData("5.6.7.8")) + inputChange = AddChangeInput("shared-add.shared", RecordType.A, None, ttl, AData("5.6.7.8")) ) val result = underTest.validateChangesWithContext( @@ -2590,35 +2596,35 @@ class BatchChangeValidationsSpec val addA = AddChangeForValidation( okZone, "dotted.a", - AddChangeInput("dotted.a.ok.", RecordType.A, ttl, AData("1.1.1.1")), + AddChangeInput("dotted.a.ok.", RecordType.A, None, ttl, AData("1.1.1.1")), defaultTtl ) val addAAAA = AddChangeForValidation( okZone, "dotted.aaaa", - AddChangeInput("dotted.aaaa.ok.", RecordType.AAAA, ttl, AAAAData("1:2:3:4:5:6:7:8")), + AddChangeInput("dotted.aaaa.ok.", RecordType.AAAA, None, ttl, AAAAData("1:2:3:4:5:6:7:8")), defaultTtl ) val addCNAME = AddChangeForValidation( okZone, "dotted.cname", - AddChangeInput("dotted.cname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("foo.com"))), + AddChangeInput("dotted.cname.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("foo.com"))), defaultTtl ) val addMX = AddChangeForValidation( okZone, "dotted.mx", - AddChangeInput("dotted.mx.ok.", RecordType.MX, ttl, MXData(1, Fqdn("foo.bar."))), + AddChangeInput("dotted.mx.ok.", RecordType.MX, None, ttl, MXData(1, Fqdn("foo.bar."))), defaultTtl ) val addTXT = AddChangeForValidation( okZone, "dotted.txt", - AddChangeInput("dotted.txt.ok.", RecordType.TXT, ttl, TXTData("test")), + AddChangeInput("dotted.txt.ok.", RecordType.TXT, None, ttl, TXTData("test")), defaultTtl ) @@ -2649,27 +2655,27 @@ class BatchChangeValidationsSpec val deleteA = DeleteRRSetChangeForValidation( okZone, "existing.dotted.a", - DeleteRRSetChangeInput("existing.dotted.a.ok.", RecordType.A) + DeleteRRSetChangeInput("existing.dotted.a.ok.", RecordType.A, None) ) val deleteAAAA = DeleteRRSetChangeForValidation( okZone, "existing.dotted.aaaa", - DeleteRRSetChangeInput("existing.dotted.aaaa.ok.", RecordType.AAAA) + DeleteRRSetChangeInput("existing.dotted.aaaa.ok.", RecordType.AAAA, None) ) val deleteCname = DeleteRRSetChangeForValidation( okZone, "existing.dotted.cname", - DeleteRRSetChangeInput("existing.dotted.cname.ok.", RecordType.CNAME) + DeleteRRSetChangeInput("existing.dotted.cname.ok.", RecordType.CNAME, None) ) val deleteMX = DeleteRRSetChangeForValidation( okZone, "existing.dotted.mx", - DeleteRRSetChangeInput("existing.dotted.mx.ok.", RecordType.MX) + DeleteRRSetChangeInput("existing.dotted.mx.ok.", RecordType.MX, None) ) val deleteTXT = DeleteRRSetChangeForValidation( okZone, "existing.dotted.txt", - DeleteRRSetChangeInput("existing.dotted.txt.ok.", RecordType.TXT) + DeleteRRSetChangeInput("existing.dotted.txt.ok.", RecordType.TXT, None) ) val result = validateChangesWithContext( ChangeForValidationMap( @@ -2704,33 +2710,33 @@ class BatchChangeValidationsSpec val deleteA = DeleteRRSetChangeForValidation( okZone, "existing.dotted.a", - DeleteRRSetChangeInput("existing.dotted.a.ok.", RecordType.A) + DeleteRRSetChangeInput("existing.dotted.a.ok.", RecordType.A, None) ) val deleteAAAA = DeleteRRSetChangeForValidation( okZone, "existing.dotted.aaaa", - DeleteRRSetChangeInput("existing.dotted.aaaa.ok.", RecordType.AAAA) + DeleteRRSetChangeInput("existing.dotted.aaaa.ok.", RecordType.AAAA, None) ) val deleteCname = DeleteRRSetChangeForValidation( okZone, "existing.dotted.cname", - DeleteRRSetChangeInput("existing.dotted.cname.ok.", RecordType.CNAME) + DeleteRRSetChangeInput("existing.dotted.cname.ok.", RecordType.CNAME, None) ) val deleteMX = DeleteRRSetChangeForValidation( okZone, "existing.dotted.mx", - DeleteRRSetChangeInput("existing.dotted.mx.ok.", RecordType.MX) + DeleteRRSetChangeInput("existing.dotted.mx.ok.", RecordType.MX, None) ) val deleteTXT = DeleteRRSetChangeForValidation( okZone, "existing.dotted.txt", - DeleteRRSetChangeInput("existing.dotted.txt.ok.", RecordType.TXT) + DeleteRRSetChangeInput("existing.dotted.txt.ok.", RecordType.TXT, None) ) val addUpdateA = AddChangeForValidation( okZone, "existing.dotted.a", - AddChangeInput("existing.dotted.a.ok.", RecordType.A, ttl, AData("1.2.3.4")), + AddChangeInput("existing.dotted.a.ok.", RecordType.A, None, ttl, AData("1.2.3.4")), defaultTtl ) val addUpdateAAAA = AddChangeForValidation( @@ -2739,6 +2745,7 @@ class BatchChangeValidationsSpec AddChangeInput( "existing.dotted.aaaa.ok.", RecordType.AAAA, + None, Some(700), AAAAData("1:2:3:4:5:6:7:8") ), @@ -2750,6 +2757,7 @@ class BatchChangeValidationsSpec AddChangeInput( "existing.dotted.cname.ok.", RecordType.CNAME, + None, Some(700), CNAMEData(Fqdn("test")) ), @@ -2758,13 +2766,13 @@ class BatchChangeValidationsSpec val addUpdateMX = AddChangeForValidation( okZone, "existing.dotted.mx", - AddChangeInput("existing.dotted.mx.ok.", RecordType.MX, Some(700), MXData(3, Fqdn("mx"))), + AddChangeInput("existing.dotted.mx.ok.", RecordType.MX, None, Some(700), MXData(3, Fqdn("mx"))), defaultTtl ) val addUpdateTXT = AddChangeForValidation( okZone, "existing.dotted.txt", - AddChangeInput("existing.dotted.txt.ok.", RecordType.TXT, Some(700), TXTData("testing")), + AddChangeInput("existing.dotted.txt.ok.", RecordType.TXT, None, Some(700), TXTData("testing")), defaultTtl ) @@ -2797,17 +2805,17 @@ class BatchChangeValidationsSpec } property("validateAddChangeInput: should fail for a CNAME addChangeInput with forward slash for forward zone") { - val cnameWithForwardSlash = AddChangeInput("cname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname/"))) + val cnameWithForwardSlash = AddChangeInput("cname.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("cname/"))) val result = validateAddChangeInput(cnameWithForwardSlash, false) result should haveInvalid[DomainValidationError](InvalidCname("cname/.",false)) } property("validateAddChangeInput: should succeed for a valid CNAME addChangeInput without forward slash for forward zone") { - val cname = AddChangeInput("cname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname"))) + val cname = AddChangeInput("cname.ok.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("cname"))) val result = validateAddChangeInput(cname, false) result shouldBe valid } property("validateAddChangeInput: should succeed for a valid CNAME addChangeInput with forward slash for reverse zone") { - val cnameWithForwardSlash = AddChangeInput("2.0.192.in-addr.arpa.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname/"))) + val cnameWithForwardSlash = AddChangeInput("2.0.192.in-addr.arpa.", RecordType.CNAME, None, ttl, CNAMEData(Fqdn("cname/"))) val result = validateAddChangeInput(cnameWithForwardSlash, true) result shouldBe valid } diff --git a/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala b/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala index 2b713b5e2..2d76e6f88 100644 --- a/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/domain/record/RecordSetServiceSpec.scala @@ -1951,19 +1951,22 @@ class RecordSetServiceSpec val completeRecordSetChanges: List[RecordSetChange] = List(pendingCreateAAAA, pendingCreateCNAME, completeCreateAAAA, completeCreateCNAME) + doReturn(IO.pure(Some(zoneActive))) + .when(mockZoneRepo) + .getZone(zoneActive.id) doReturn(IO.pure(ListRecordSetChangesResults(completeRecordSetChanges))) .when(mockRecordChangeRepo) - .listRecordSetChanges(zoneId = Some(okZone.id), startFrom = None, maxItems = 100, fqdn = None, recordType = None) + .listRecordSetChanges(zoneId = Some(zoneActive.id), startFrom = None, maxItems = 100, fqdn = None, recordType = None) doReturn(IO.pure(ListUsersResults(Seq(okUser), None))) .when(mockUserRepo) .getUsers(any[Set[String]], any[Option[String]], any[Option[Int]]) val result: ListRecordSetChangesResponse = - underTest.listRecordSetChanges(Some(okZone.id), authPrincipal = okAuth).value.unsafeRunSync().toOption.get + underTest.listRecordSetChanges(zoneActive.id, authPrincipal = okAuth).value.unsafeRunSync().toOption.get val changesWithName = completeRecordSetChanges.map(change => RecordSetChangeInfo(change, Some("ok"))) val expectedResults = ListRecordSetChangesResponse( - zoneId = okZone.id, + zoneId = zoneActive.id, recordSetChanges = changesWithName, nextId = None, startFrom = None, @@ -2010,7 +2013,7 @@ class RecordSetServiceSpec .getUsers(any[Set[String]], any[Option[String]], any[Option[Int]]) val result: ListRecordSetChangesResponse = - underTest.listRecordSetChanges(Some(okZone.id), authPrincipal = okAuth).value.unsafeRunSync().toOption.get + underTest.listRecordSetChanges(okZone.id, authPrincipal = okAuth).value.unsafeRunSync().toOption.get val expectedResults = ListRecordSetChangesResponse( zoneId = okZone.id, recordSetChanges = List(), @@ -2076,7 +2079,7 @@ class RecordSetServiceSpec "return a NotAuthorizedError" in { val error = - underTest.listRecordSetChanges(Some(zoneNotAuthorized.id), authPrincipal = okAuth).value.unsafeRunSync().swap.toOption.get + underTest.listRecordSetChanges(zoneNotAuthorized.id, authPrincipal = okAuth).value.unsafeRunSync().swap.toOption.get error shouldBe a[NotAuthorizedError] } @@ -2093,7 +2096,7 @@ class RecordSetServiceSpec .getUsers(any[Set[String]], any[Option[String]], any[Option[Int]]) val result: ListRecordSetChangesResponse = - underTest.listRecordSetChanges(Some(okZone.id), authPrincipal = okAuth).value.unsafeRunSync().toOption.get + underTest.listRecordSetChanges(okZone.id, authPrincipal = okAuth).value.unsafeRunSync().toOption.get val changesWithName = List(RecordSetChangeInfo(rsChange2, Some("ok")), RecordSetChangeInfo(rsChange1, Some("ok"))) val expectedResults = ListRecordSetChangesResponse( diff --git a/modules/api/src/test/scala/vinyldns/api/route/BatchChangeJsonProtocolSpec.scala b/modules/api/src/test/scala/vinyldns/api/route/BatchChangeJsonProtocolSpec.scala index 3935418bd..53ad40cb0 100644 --- a/modules/api/src/test/scala/vinyldns/api/route/BatchChangeJsonProtocolSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/route/BatchChangeJsonProtocolSpec.scala @@ -127,15 +127,15 @@ class BatchChangeJsonProtocolSpec addCNAMEChangeInputJson ) - val addAChangeInput = AddChangeInput("foo.", A, Some(3600), AData("1.1.1.1")) + val addAChangeInput = AddChangeInput("foo.", A, None, Some(3600), AData("1.1.1.1")) - val deleteAChangeInput = DeleteRRSetChangeInput("foo.", A) + val deleteAChangeInput = DeleteRRSetChangeInput("foo.", A, None) - val addAAAAChangeInput = AddChangeInput("bar.", AAAA, Some(1200), AAAAData("1:2:3:4:5:6:7:8")) + val addAAAAChangeInput = AddChangeInput("bar.", AAAA, None, Some(1200), AAAAData("1:2:3:4:5:6:7:8")) - val addCNAMEChangeInput = AddChangeInput("bizz.baz.", CNAME, Some(200), CNAMEData(Fqdn("buzz."))) + val addCNAMEChangeInput = AddChangeInput("bizz.baz.", CNAME, None, Some(200), CNAMEData(Fqdn("buzz."))) - val addPTRChangeInput = AddChangeInput("4.5.6.7", PTR, Some(200), PTRData(Fqdn("test.com."))) + val addPTRChangeInput = AddChangeInput("4.5.6.7", PTR, None, Some(200), PTRData(Fqdn("test.com."))) val fooDiscoveryError = ZoneDiscoveryError("foo.") @@ -211,7 +211,7 @@ class BatchChangeJsonProtocolSpec ) val result = ChangeInputSerializer.fromJson(json).value - result shouldBe AddChangeInput("foo.", A, None, AData("1.1.1.1")) + result shouldBe AddChangeInput("foo.", A, None, None, AData("1.1.1.1")) } "return an error if the record is not specified for add" in { @@ -250,7 +250,7 @@ class BatchChangeJsonProtocolSpec "serializing ChangeInputSerializer to JSON" should { "successfully serialize valid data for delete" in { - val deleteChangeInput = DeleteRRSetChangeInput("foo.", A, Some(AData("1.1.1.1"))) + val deleteChangeInput = DeleteRRSetChangeInput("foo.", A, None, Some(AData("1.1.1.1"))) val json: JObject = buildDeleteRRSetInputJson(Some("foo."), Some(A), Some(AData("1.1.1.1"))) val result = ChangeInputSerializer.toJson(deleteChangeInput) diff --git a/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala b/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala index 2aea6247b..2e997702e 100644 --- a/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala +++ b/modules/api/src/test/scala/vinyldns/api/route/RecordSetRoutingSpec.scala @@ -758,16 +758,14 @@ class RecordSetRoutingSpec }.toResult def listRecordSetChanges( - zoneId: Option[String], + zoneId: String, startFrom: Option[Int], maxItems: Int, - fqdn: Option[String], - recordType: Option[RecordType], authPrincipal: AuthPrincipal ): Result[ListRecordSetChangesResponse] = { zoneId match { - case Some(zoneNotFound.id) => Left(ZoneNotFoundError(s"$zoneId")) - case Some(notAuthorizedZone.id) => Left(NotAuthorizedError("no way")) + case zoneNotFound.id => Left(ZoneNotFoundError(s"$zoneId")) + case notAuthorizedZone.id => Left(NotAuthorizedError("no way")) case _ => Right(listRecordSetChangesResponse) } }.toResult diff --git a/modules/api/src/universal/conf/application.conf b/modules/api/src/universal/conf/application.conf index ff70827df..56008f0a7 100644 --- a/modules/api/src/universal/conf/application.conf +++ b/modules/api/src/universal/conf/application.conf @@ -352,6 +352,10 @@ akka.http { # Set to `infinite` to disable. bind-timeout = 5s + # A default request timeout is applied globally to all routes and can be configured using the + # akka.http.server.request-timeout setting (which defaults to 20 seconds). + # request-timeout = 60s + # Show verbose error messages back to the client verbose-error-messages = on } diff --git a/modules/api/src/universal/conf/logback.xml b/modules/api/src/universal/conf/logback.xml index 688a07b6b..4e4f1f6aa 100644 --- a/modules/api/src/universal/conf/logback.xml +++ b/modules/api/src/universal/conf/logback.xml @@ -7,7 +7,7 @@ - + diff --git a/modules/core/src/main/scala/vinyldns/core/domain/batch/SingleChange.scala b/modules/core/src/main/scala/vinyldns/core/domain/batch/SingleChange.scala index 254246ebf..87cc761ed 100644 --- a/modules/core/src/main/scala/vinyldns/core/domain/batch/SingleChange.scala +++ b/modules/core/src/main/scala/vinyldns/core/domain/batch/SingleChange.scala @@ -46,11 +46,11 @@ sealed trait SingleChange { delete.copy(status = SingleChangeStatus.Failed, systemMessage = Some(error)) } - def withDoesNotExistMessage(error: String): SingleChange = this match { + def withDoesNotExistMessage: SingleChange = this match { case add: SingleAddChange => - add.copy(status = SingleChangeStatus.Failed, systemMessage = Some(error)) + add.copy(status = SingleChangeStatus.Failed) case delete: SingleDeleteRRSetChange => - delete.copy(status = SingleChangeStatus.Complete, systemMessage = Some(error)) + delete.copy(status = SingleChangeStatus.Complete) } def withProcessingError(message: Option[String], failedRecordChangeId: String): SingleChange = diff --git a/modules/docs/src/main/mdoc/operator/config-api.md b/modules/docs/src/main/mdoc/operator/config-api.md index 37026de6c..c25243e51 100644 --- a/modules/docs/src/main/mdoc/operator/config-api.md +++ b/modules/docs/src/main/mdoc/operator/config-api.md @@ -990,7 +990,10 @@ dotted-hosts = { # The time period within which the TCP binding process must be completed. # Set to `infinite` to disable. bind-timeout = 5s - + # A default request timeout is applied globally to all routes and can be configured using the + # akka.http.server.request-timeout setting (which defaults to 20 seconds). + # request-timeout = 60s + # Show verbose error messages back to the client verbose-error-messages = on } diff --git a/modules/docs/src/main/mdoc/operator/setup-ldap.md b/modules/docs/src/main/mdoc/operator/setup-ldap.md index 1ccf61116..c2b4a8606 100644 --- a/modules/docs/src/main/mdoc/operator/setup-ldap.md +++ b/modules/docs/src/main/mdoc/operator/setup-ldap.md @@ -17,7 +17,7 @@ can read data from the Directory. Once you have that information, proceed to th **Considerations** You _should_ communicate to your Directory over LDAP using TLS. To do so, the SSL certs should be installed on the portal servers, or provided via a java trust store (key store). The portal provides an option to specific -a java key store when it starts up. +a java key store when it starts up. For more information: [Using Java Key Store In VinylDNS](https://github.com/vinyldns/vinyldns/tree/master/modules/portal#building-locally) ## Configuring LDAP Before you can configure LDAP, make note of the host, username, and password that you will be using. diff --git a/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordChangeRepositoryIntegrationSpec.scala b/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordChangeRepositoryIntegrationSpec.scala index 397db1674..c6636c7d2 100644 --- a/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordChangeRepositoryIntegrationSpec.scala +++ b/modules/mysql/src/it/scala/vinyldns/mysql/repository/MySqlRecordChangeRepositoryIntegrationSpec.scala @@ -216,13 +216,11 @@ class MySqlRecordChangeRepositoryIntegrationSpec } saveRecChange.attempt.unsafeRunSync() shouldBe right val page1 = repo.listFailedRecordSetChanges(Some(okZone.id), 2, 0).unsafeRunSync() - println(page1.items) page1.nextId shouldBe 3 page1.maxItems shouldBe 2 (page1.items should contain).theSameElementsInOrderAs(expectedOrder.take(2)) val page2 = repo.listFailedRecordSetChanges(Some(okZone.id), 2, page1.nextId).unsafeRunSync() - println(page2.items) page2.nextId shouldBe 6 page2.maxItems shouldBe 2 (page2.items should contain).theSameElementsInOrderAs(expectedOrder.slice(3, 5)) diff --git a/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlBatchChangeRepository.scala b/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlBatchChangeRepository.scala index f16053de2..74c029939 100644 --- a/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlBatchChangeRepository.scala +++ b/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlBatchChangeRepository.scala @@ -17,9 +17,9 @@ package vinyldns.mysql.repository import java.sql.Timestamp - import cats.data._ import cats.effect._ + import java.time.Instant import org.slf4j.LoggerFactory import scalikejdbc._ @@ -166,8 +166,8 @@ class MySqlBatchChangeRepository } def getBatchFromSingleChangeId( - singleChangeId: String - )(implicit s: DBSession): Option[BatchChange] = + singleChangeId: String + )(implicit s: DBSession): Option[BatchChange] = GET_BATCH_CHANGE_METADATA_FROM_SINGLE_CHANGE .bind(singleChangeId) .map(extractBatchChange(None)) @@ -181,12 +181,9 @@ class MySqlBatchChangeRepository .apply() batchMeta.copy(changes = changes) } - monitor("repo.BatchChangeJDBC.updateSingleChanges") { IO { - logger.info( - s"Updating single change statuses: ${singleChanges.map(ch => (ch.id, ch.status))}" - ) + logger.info(s"Updating single change status: ${singleChanges.map(ch => (ch.id, ch.status))}") DB.localTx { implicit s => for { headChange <- singleChanges.headOption @@ -194,8 +191,7 @@ class MySqlBatchChangeRepository _ = UPDATE_SINGLE_CHANGE.batchByName(batchParams: _*).apply() batchChange <- getBatchFromSingleChangeId(headChange.id) } yield batchChange - } - } + }} } } @@ -394,7 +390,6 @@ class MySqlBatchChangeRepository case Left(e) => throw e } } - PUT_SINGLE_CHANGE.batchByName(singleChangesParams: _*).apply() batchChange } diff --git a/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordChangeRepository.scala b/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordChangeRepository.scala index 47e085cc8..130b89363 100644 --- a/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordChangeRepository.scala +++ b/modules/mysql/src/main/scala/vinyldns/mysql/repository/MySqlRecordChangeRepository.scala @@ -23,7 +23,7 @@ import vinyldns.core.domain.record.RecordType.RecordType import vinyldns.core.domain.record._ import vinyldns.core.protobuf.ProtobufConversions import vinyldns.core.route.Monitored -import vinyldns.mysql.repository.MySqlRecordSetRepository.fromRecordType +import vinyldns.mysql.repository.MySqlRecordSetRepository.{fromRecordType, toFQDN} import vinyldns.proto.VinylDNSProto class MySqlRecordChangeRepository @@ -103,7 +103,7 @@ class MySqlRecordChangeRepository change.zoneId, change.created.toEpochMilli, fromChangeType(change.changeType), - if(change.recordSet.name == change.zone.name) change.zone.name else change.recordSet.name + "." + change.zone.name, + toFQDN(change.zone.name, change.recordSet.name), fromRecordType(change.recordSet.typ), toPB(change).toByteArray, ) diff --git a/modules/portal/app/views/dnsChanges/dnsChangeNew.scala.html b/modules/portal/app/views/dnsChanges/dnsChangeNew.scala.html index 712de04c2..4ca86c4c9 100644 --- a/modules/portal/app/views/dnsChanges/dnsChangeNew.scala.html +++ b/modules/portal/app/views/dnsChanges/dnsChangeNew.scala.html @@ -326,7 +326,7 @@ - +

See documentation for sample CSV

Limit reached. Cannot add more than {{batchChangeLimit}} records per DNS change.

@@ -336,6 +336,9 @@
+ {{ confirmationPrompt }} There were errors, please review the highlighted rows and then proceed. diff --git a/modules/portal/app/views/groups/groups.scala.html b/modules/portal/app/views/groups/groups.scala.html index b324c1551..811f2f4da 100644 --- a/modules/portal/app/views/groups/groups.scala.html +++ b/modules/portal/app/views/groups/groups.scala.html @@ -347,7 +347,7 @@ name="name" class="form-control" ng-model="currentGroup.name" - ng-model-options="{ updateOn: 'submit' }" + ng-change="checkForChanges()" type="text" required> @@ -360,7 +360,7 @@ name="email" class="form-control" ng-model="currentGroup.email" - ng-model-options="{ updateOn: 'submit' }" + ng-change="checkForChanges()" type="text" required> @@ -389,7 +389,7 @@ name="description" class="form-control" ng-model="currentGroup.description" - ng-model-options="{ updateOn: 'submit' }" + ng-change="checkForChanges()" type="text"> @@ -399,7 +399,7 @@
diff --git a/modules/portal/app/views/zones/zoneTabs/manageRecords.scala.html b/modules/portal/app/views/zones/zoneTabs/manageRecords.scala.html index aa05eae95..3da87bbda 100644 --- a/modules/portal/app/views/zones/zoneTabs/manageRecords.scala.html +++ b/modules/portal/app/views/zones/zoneTabs/manageRecords.scala.html @@ -67,6 +67,7 @@
+ @@ -86,6 +87,9 @@
+ + + diff --git a/modules/portal/app/views/zones/zones.scala.html b/modules/portal/app/views/zones/zones.scala.html index ac3b0ca86..662de3669 100644 --- a/modules/portal/app/views/zones/zones.scala.html +++ b/modules/portal/app/views/zones/zones.scala.html @@ -282,12 +282,10 @@
-
- - @@ -295,7 +293,7 @@
- + @@ -304,192 +302,184 @@
- - - -
- -
-
-
-

Loading my deleted zones...

-

No zones match the search criteria.

- - -
- {{ getZonesPageNumber("myDeletedZones") }} - -
- - - - - - - - - - - - - @if(meta.sharedDisplayEnabled) { - - } - - - - - - - - - - - - @if(meta.sharedDisplayEnabled) { - - } - - -
NameEmailAdmin GroupCreatedAbandonedStatusAbandoned ByAccess
- - - - - - {{deletedZone.zoneChange.zone.created}} - - {{deletedZone.zoneChange.zone.updated}} - - {{deletedZone.userName}} - {{zone.shared ? "Shared" : "Private"}}
- - -
- {{ getZonesPageNumber("myDeletedZones") }} - -
- - -
-
-
-
-

Loading all deleted zones...

-

No zones match the search criteria.

- - -
- {{ getZonesPageNumber("allDeletedZones") }} - -
- - - - - - - - - - - - - @if(meta.sharedDisplayEnabled) { - - } - - - - - - - - - - - - @if(meta.sharedDisplayEnabled) { - - } - - -
NameEmailAdmin GroupCreatedAbandonedStatusAbandoned ByAccess
- - - - - - {{deletedZone.zoneChange.zone.created}} - - {{deletedZone.zoneChange.zone.updated}} - - {{deletedZone.userName}} - {{zone.shared ? "Shared" : "Private"}}
- - -
- {{ getZonesPageNumber("allDeletedZones") }} - -
- - -
-
-
-
-
+ + + +
+ + +
+ {{ getZonesPageNumber("myDeletedZones") }} + +
+ + +
+ {{ getZonesPageNumber("allDeletedZones") }} + +
+ + +
+
+
+

Loading my deleted zones...

+

No zones match the search criteria.

+ + + + + + + + + + + @if(meta.sharedDisplayEnabled) { + + } + + + + + + + + + + + + @if(meta.sharedDisplayEnabled) { + + } + + +
NameEmailAdmin GroupCreatedAbandonedStatusAbandoned ByAccess
+ + + + + + {{deletedZone.zoneChange.zone.created}} + + {{deletedZone.zoneChange.zone.updated}} + + {{deletedZone.userName}} + {{deletedZone.zoneChange.zone.shared ? "Shared" : "Private"}}
+ + +
+ {{ getZonesPageNumber("myDeletedZones") }} + +
+ +
+
+
+
+

Loading all deleted zones...

+

No zones match the search criteria.

+ + + + + + + + + + + @if(meta.sharedDisplayEnabled) { + + } + + + + + + + + + + + + @if(meta.sharedDisplayEnabled) { + + } + + +
NameEmailAdmin GroupCreatedAbandonedStatusAbandoned ByAccess
+ + + + + + {{deletedZone.zoneChange.zone.created}} + + {{deletedZone.zoneChange.zone.updated}} + + {{deletedZone.userName}} + {{deletedZone.zoneChange.zone.shared ? "Shared" : "Private"}}
+ + +
+ {{ getZonesPageNumber("allDeletedZones") }} + +
+ +
+
+
+
+
+
-
- -
-
diff --git a/modules/portal/conf/application.conf b/modules/portal/conf/application.conf index fc77008b3..fea91a5e8 100644 --- a/modules/portal/conf/application.conf +++ b/modules/portal/conf/application.conf @@ -88,6 +88,9 @@ api { } } +# Batch change limit +batch-change-limit = 1000 + http.port = 9001 http.port = ${?PORTAL_PORT} diff --git a/modules/portal/conf/logback.xml b/modules/portal/conf/logback.xml index 1eea934d5..415fe95b5 100644 --- a/modules/portal/conf/logback.xml +++ b/modules/portal/conf/logback.xml @@ -15,7 +15,7 @@ - + diff --git a/modules/portal/public/css/vinyldns.css b/modules/portal/public/css/vinyldns.css index 601a17b92..4beac8b20 100644 --- a/modules/portal/public/css/vinyldns.css +++ b/modules/portal/public/css/vinyldns.css @@ -462,6 +462,10 @@ input[type="file"] { margin-right: 10px; } +.vinyldns_paginate_button { + margin-right: 8px; +} + .no-top-margin { margin-top: 0px; } diff --git a/modules/portal/public/lib/controllers/controller.groups.js b/modules/portal/public/lib/controllers/controller.groups.js index 16244fc70..c35d961bf 100644 --- a/modules/portal/public/lib/controllers/controller.groups.js +++ b/modules/portal/public/lib/controllers/controller.groups.js @@ -210,8 +210,8 @@ angular.module('controller.groups', []).controller('GroupsController', function handleError(error, 'groupsService::getGroups-failure'); }); } - //Function for fetching list of valid domains + //Function for fetching list of valid domains $scope.validDomains=function getValidEmailDomains() { function success(response) { $log.debug('groupsService::listEmailDomains-success', response); @@ -247,10 +247,25 @@ angular.module('controller.groups', []).controller('GroupsController', function $scope.editGroup = function (groupInfo) { $scope.currentGroup = groupInfo; + $scope.initialGroup = { + name: $scope.currentGroup.name, + email: $scope.currentGroup.email, + description: $scope.currentGroup.description + }; + $scope.hasChanges = false; $scope.validDomains(); $("#modal_edit_group").modal("show"); }; + // Function to check for changes + $scope.checkForChanges = function() { + $scope.hasChanges = + $scope.currentGroup.name !== $scope.initialGroup.name || + $scope.currentGroup.email !== $scope.initialGroup.email || + ($scope.currentGroup.description !== $scope.initialGroup.description && + !($scope.currentGroup.description === "" && $scope.initialGroup.description === undefined)); + }; + $scope.getGroupAndUpdate = function(groupId, name, email, description) { function success(response) { $log.debug('groupsService::getGroup-success'); @@ -281,6 +296,7 @@ angular.module('controller.groups', []).controller('GroupsController', function return groupsService.updateGroup(groupId, payload) .then(success) .catch(function (error) { + $scope.closeEditModal(); handleError(error, 'groupsService::updateGroup-failure'); }); } diff --git a/modules/portal/public/lib/dns-change/dns-change-new.controller.js b/modules/portal/public/lib/dns-change/dns-change-new.controller.js index e5ab61659..931426f9a 100644 --- a/modules/portal/public/lib/dns-change/dns-change-new.controller.js +++ b/modules/portal/public/lib/dns-change/dns-change-new.controller.js @@ -44,6 +44,7 @@ $scope.manualReviewEnabled; $scope.naptrFlags = ["U", "S", "A", "P"]; + $scope.addSingleChange = function() { $scope.newBatch.changes.push({changeType: "Add", type: "A+PTR"}); var changesLength = $scope.newBatch.changes.length; @@ -116,7 +117,7 @@ $scope.alerts.push(alert); $timeout(function(){ location.href = "/dnschanges/" + response.data.id; - }, 2000); + }, 2000); $scope.batch = response.data; } @@ -161,14 +162,14 @@ $scope.alerts.push(alert); } - $scope.uploadCSV = function(file) { - parseFile(file).then(function(dataLength){ - $scope.alerts.push({type: 'success', content: 'Successfully imported ' + dataLength + ' changes.' }); + $scope.uploadCSV = function(file, batchChangeLimit) { + parseFile(file, batchChangeLimit).then(function(dataLength){ + $scope.alerts.push({type: 'success', content: 'Successfully imported ' + dataLength + ' DNS changes.' }); }, function(error) { $scope.alerts.push({type: 'danger', content: error}); }); - function parseFile(file) { + function parseFile(file, batchChangeLimit) { return $q(function(resolve, reject) { if (!file.name.endsWith('.csv')) { reject("Import failed. File should be of ‘.csv’ type."); @@ -177,6 +178,9 @@ var reader = new FileReader(); reader.onload = function(e) { var rows = e.target.result.split("\n"); + if(rows.length - 1 > batchChangeLimit) + {reject("Import failed. Cannot add more than " + batchChangeLimit + " records per DNS change."); + } else { if (rows[0].trim() == "Change Type,Record Type,Input Name,TTL,Record Data") { $scope.newBatch.changes = []; for(var i = 1; i < rows.length; i++) { @@ -186,10 +190,10 @@ } $scope.$apply() resolve($scope.newBatch.changes.length); - } else { + } else { reject("Import failed. CSV header must be: Change Type,Record Type,Input Name,TTL,Record Data"); } - } + }} reader.readAsText(file); } }); diff --git a/modules/portal/public/lib/dns-change/dns-change.service.js b/modules/portal/public/lib/dns-change/dns-change.service.js index 8103e1e2d..448e9a3b2 100644 --- a/modules/portal/public/lib/dns-change/dns-change.service.js +++ b/modules/portal/public/lib/dns-change/dns-change.service.js @@ -30,7 +30,24 @@ "allowManualReview": allowManualReview } var url = utilityService.urlBuilder('/api/dnschanges', params); - return $http.post(url, data, {headers: utilityService.getCsrfHeader()}); + let loader = $("#loader"); + loader.modal({ + backdrop: "static", + keyboard: false, //remove option to close with keyboard + show: true //Display loader! + }) + let promis = $http.post(url, data, {headers: utilityService.getCsrfHeader()}); + + function hideLoader() { + loader.modal("hide"); + + // Manually remove the backdrop after the modal is hidden + $('.modal-backdrop').remove(); + $('body').removeClass('modal-open'); // Remove the class that prevents scrolling + } + // Hide loader when api gets response + promis.then(hideLoader, hideLoader).catch(hideLoader).finally(hideLoader); + return promis }; this.getBatchChanges = function (maxItems, startFrom, ignoreAccess, approvalStatus, userName, dateTimeRangeStart, dateTimeRangeEnd) { diff --git a/modules/portal/public/lib/recordset/recordsets.controller.js b/modules/portal/public/lib/recordset/recordsets.controller.js index 256a1e95a..2fac5cd5a 100644 --- a/modules/portal/public/lib/recordset/recordsets.controller.js +++ b/modules/portal/public/lib/recordset/recordsets.controller.js @@ -279,7 +279,7 @@ $scope.changeHistoryPrevPage = function() { var startFrom = pagingService.getPrevStartFrom(changePaging); return recordsService - .listRecordSetChangeHistory(changePaging.maxItems, startFrom, $scope.recordFqdn, $scope.recordType) + .listRecordSetChangeHistory($scope.zoneId, changePaging.maxItems, startFrom, $scope.recordFqdn, $scope.recordType) .then(function(response) { changePaging = pagingService.prevPageUpdate(response.data.nextId, changePaging); updateChangeDisplay(response.data.recordSetChanges); @@ -291,7 +291,7 @@ $scope.changeHistoryNextPage = function() { return recordsService - .listRecordSetChangeHistory(changePaging.maxItems, changePaging.next, $scope.recordFqdn, $scope.recordType) + .listRecordSetChangeHistory($scope.zoneId, changePaging.maxItems, changePaging.next, $scope.recordFqdn, $scope.recordType) .then(function(response) { var changes = response.data.recordSetChanges; changePaging = pagingService.nextPageUpdate(changes, response.data.nextId, changePaging); diff --git a/modules/portal/public/lib/services/records/service.records.js b/modules/portal/public/lib/services/records/service.records.js index f8505e1e1..f5b5be1c3 100644 --- a/modules/portal/public/lib/services/records/service.records.js +++ b/modules/portal/public/lib/services/records/service.records.js @@ -77,7 +77,16 @@ angular.module('service.records', []) "recordTypeSort": recordTypeSort }; var url = utilityService.urlBuilder("/api/zones/"+id+"/recordsets", params); - return $http.get(url); + let loader = $("#loader"); + loader.modal({ + backdrop: "static", //remove ability to close modal with click + keyboard: false, //remove option to close with keyboard + show: true //Display loader! + }) + let promis = $http.get(url); + // Hide loader when api gets response + promis.then(()=>loader.modal("hide"), ()=>loader.modal("hide")) + return promis; }; this.getRecordSet = function (rsid) { diff --git a/quickstart/docker-compose.yml b/quickstart/docker-compose.yml index 7cdca0699..315d93fb8 100644 --- a/quickstart/docker-compose.yml +++ b/quickstart/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: # LDAP container hosting example users @@ -9,7 +7,7 @@ services: ports: - "19004:19004" - # Integration image hosting r53, sns, sqs, bind, and mysql + # Integration image hosting r53, sns, sqs, bind and mysql integration: container_name: "vinyldns-api-integration" hostname: "vinyldns-integration" diff --git a/test/api/functional/application.conf b/test/api/functional/application.conf index e0f35df07..5c7f48aa3 100644 --- a/test/api/functional/application.conf +++ b/test/api/functional/application.conf @@ -302,6 +302,10 @@ akka.http { # Set to `infinite` to disable. bind-timeout = 5s + # A default request timeout is applied globally to all routes and can be configured using the + # akka.http.server.request-timeout setting (which defaults to 20 seconds). + # request-timeout = 60s + # Show verbose error messages back to the client verbose-error-messages = on } diff --git a/test/api/integration/application.conf b/test/api/integration/application.conf index c09038a85..e7c8c20e1 100644 --- a/test/api/integration/application.conf +++ b/test/api/integration/application.conf @@ -360,6 +360,10 @@ akka.http { # Set to `infinite` to disable. bind-timeout = 5s + # A default request timeout is applied globally to all routes and can be configured using the + # akka.http.server.request-timeout setting (which defaults to 20 seconds). + # request-timeout = 60s + # Show verbose error messages back to the client verbose-error-messages = on } diff --git a/version.sbt b/version.sbt index 163659bda..f2566e80d 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.20.0" +version in ThisBuild := "0.20.2"