Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Rework delete dataset step #95

Open
wants to merge 2 commits into
base: release/v1.2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ stage ("stage-name") {
writeFileToFile destFile: "u/USER/myfile", sourceFile: "myfile.txt", binary: "true"

deleteDataset dsn:"EXAMPLE.DATASET", failOnNotExist:"False"
deleteDataset dsn:"EXAMPLE.DATASET", member:"MEMBER", failOnNotExist:"True"
deleteDataset dsn:"EXAMPLE.DATASET(MEMBER)", failOnNotExist:"True"
deleteDatasetsByMask mask:"EXAMPLE.DATASET.*", failOnNotExist:"False"
}
// ...
Expand Down Expand Up @@ -121,19 +121,19 @@ zosmf ("z/os-connection-name") {
### deleteDataset - Represents an action for deleting datasets and members in a declarative style
```groovy
zosmf ("z/os-connection-name") {
deleteDataset dsn: "EXAMPLE.DATASET", member:"MEMBER", failOnNotExist:"False"
deleteDataset dsn: "EXAMPLE.DATASET(MEMBER)", failOnNotExist: false
}
```
**Mandatory Parameters:**
* ```dsn:"EXAMPLE.DATASET"``` - Sequential or library dataset name for deletion
* ```member:"MEMBER"``` - Dataset member name for deletion
* ```failOnNotExist:"False"``` - If the dataset has been deleted and the option is enabled, execution will halt. (Boolean parameter, is set to 'False' by default)
* ```dsn:"EXAMPLE.DATASET"``` - Sequential or library dataset name in the form `HLQ.DSNAME` or `HLQ.DSNAME(MEMNAME)`
**Optional Parameters:**
* `failOnNotExist: false` - Fail the execution if the entity does not exist. Boolean parameter, is set to 'false' by default.

**Expected behavior under various deletion scenarios:**

* To delete a member from the library, the dsn and member parameters must be specified:
* To delete a member from the library, provide dataset member name in the form `HLQ.DSNAME(MEMNAME)`:
```
deleteDataset dsn:"EXAMPLE.DATASET", member:"MEMBER", failOnNotExist:"False"
deleteDataset dsn:"EXAMPLE.DATASET(MEMBER1)", failOnNotExist: false
```

* You cannot delete a VSAM dataset this way. Otherwise, you will get output similar to:
Expand Down
34 changes: 11 additions & 23 deletions src/main/kotlin/org/zowe/zdevops/classic/steps/DeleteDatasetStep.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
/*
* Copyright (c) 2022-2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright IBA Group 2022
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.classic.steps
Expand All @@ -21,22 +25,19 @@ import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
import org.zowe.zdevops.Messages
import org.zowe.zdevops.classic.AbstractBuildStep
import org.zowe.zdevops.logic.deleteDatasetOrMember
import org.zowe.zdevops.utils.validateDatasetName
import org.zowe.zdevops.utils.validateMemberName
import org.zowe.zdevops.utils.validateDsnOrDsnMemberName

class DeleteDatasetStep
/**
* Constructs a new instance of the DeleteDatasetStep.
*
* @param connectionName The name of the z/OS connection to use for deleting the dataset.
* @param dsn The name of the dataset to delete.
* @param member The name of the member to delete.
* @param dsn The name of the dataset to delete in the form ZOSMFAD.TEST(MEMNAME).
*/
@DataBoundConstructor
constructor(
connectionName: String,
val dsn: String,
val member: String?,
val failOnNotExist: Boolean = false ,
) : AbstractBuildStep(connectionName) {

Expand All @@ -46,35 +47,22 @@ constructor(
listener: BuildListener,
zosConnection: ZOSConnection
) {
deleteDatasetOrMember(dsn, member, zosConnection, listener, failOnNotExist)
deleteDatasetOrMember(dsn, zosConnection, listener, failOnNotExist)
}

@Extension
class DescriptorImpl :
Companion.DefaultBuildDescriptor(Messages.zdevops_classic_deleteDatasetStep_display_name()) {

/**
* Checks if the dataset name is valid
* Checks if the dataset name or dataset member name is valid
*
* @param dsn The dataset name
* @param dsn The dataset name or dataset member name
* @return FormValidation.ok() if the dataset name is valid, or an error message otherwise
*/
fun doCheckDsn(@QueryParameter dsn: String): FormValidation? {
return validateDatasetName(dsn)
return validateDsnOrDsnMemberName(dsn)
}

/**
* Checks if the member name is valid
*
* @param member The dataset member name
* @return FormValidation.ok() if either the member name is valid or is not provided, or an error message otherwise
*/
fun doCheckMember(@QueryParameter member: String): FormValidation? {
return if (member.isNotBlank()) {
validateMemberName(member)
} else {
FormValidation.ok()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
/*
* Copyright (c) 2022-2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright IBA Group 2022
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.declarative.jobs
Expand Down Expand Up @@ -38,9 +42,9 @@ import org.zowe.zdevops.logic.deleteDatasetOrMember
* Deleting dataset USER1A.TEST.KSDS with connection 172.20.2.2:10443
* ISRZ002 Deallocation failed - Deallocation failed for data set 'USER1A.TEST.KSDS'
* ```
* To delete a member from the library, the dsn and member parameters must be specified:
* To delete a member from the library, provide dataset member name in the form `HLQ.DSNAME(MEMNAME)`:
* ```
* deleteDataset dsn:"USER1A.TEST.LIB", member:"MEMBER1"
* deleteDataset dsn:"USER1A.TEST.LIB(MEMBER1)"
* ```
* And out will be:
* ```
Expand All @@ -57,23 +61,19 @@ import org.zowe.zdevops.logic.deleteDatasetOrMember
* Deleting dataset USER1A.DS.ISUSED.BY.USER with connection 172.20.2.2:10443
* ISRZ002 Data set in use - Data set 'USER1A.DS.ISUSED.BY.USER' in use by another user, try later or enter HELP for a list of jobs and users allocated to 'USER1A.DS.ISUSED.BY.USER'.
* ```
* It can take 2 params:
* @param dsn dataset name - sequential or library
* @param member dataset member name
* It takes:
* @param dsn dataset name - sequential or library - in the form HLQ.DSNAME or HLQ.DSNAME(MEMNAME) for member
* @param failOnNotExist Fail the execution if the entity does not exist
*/
class DeleteDatasetDeclarative @DataBoundConstructor constructor(
) : AbstractZosmfAction() {

private var dsn: String = ""
private var member: String = ""
private var failOnNotExist: Boolean = false

@DataBoundSetter
fun setDsn(dsn: String) { this.dsn = dsn }

@DataBoundSetter
fun setMember(member: String) { this.member = member }

@DataBoundSetter
fun setFailOnNotExist(failOnNotExist: Boolean) { this.failOnNotExist = failOnNotExist }

Expand All @@ -87,7 +87,7 @@ class DeleteDatasetDeclarative @DataBoundConstructor constructor(
listener: TaskListener,
zosConnection: ZOSConnection
) {
deleteDatasetOrMember(dsn, member, zosConnection, listener, failOnNotExist)
deleteDatasetOrMember(dsn, zosConnection, listener, failOnNotExist)
}

@Symbol("deleteDataset")
Expand Down
31 changes: 21 additions & 10 deletions src/main/kotlin/org/zowe/zdevops/logic/DeleteOperation.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
/*
* Copyright (c) 2022-2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright IBA Group 2022
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.logic
Expand All @@ -18,6 +22,7 @@ import org.zowe.kotlinsdk.zowe.client.sdk.zosfiles.ZosDsnList
import org.zowe.kotlinsdk.zowe.client.sdk.zosfiles.input.ListParams
import org.zowe.zdevops.declarative.jobs.zMessages
import org.zowe.zdevops.utils.runMFTryCatchWrappedQuery
import javax.naming.InvalidNameException

private val successMessage: String = zMessages.zdevops_deleting_ds_success()

Expand Down Expand Up @@ -59,27 +64,33 @@ fun deleteDatasetsByMask(mask: String, zosConnection: ZOSConnection, listener: T
/**
* Deletes a dataset or member
*
* @param dsn The dataset name.
* @param member The member name (optional).
* @param dsnWithMemName The dataset name in the form ZOSMFAD.TEST(MEMNAME).
* @param zosConnection The z/OS connection to be used for dataset deletion.
* @param listener The task listener to log information and handle exceptions.
* @throws AbortException If the dataset name is empty or the member name is invalid.
*/
fun deleteDatasetOrMember(dsn: String, member: String?, zosConnection: ZOSConnection, listener: TaskListener, failOnNotExist: Boolean) {
if (dsn.isEmpty()) {
fun deleteDatasetOrMember(dsnWithMemName: String, zosConnection: ZOSConnection, listener: TaskListener, failOnNotExist: Boolean) {
if (dsnWithMemName.isEmpty()) {
throw AbortException(zMessages.zdevops_deleting_ds_fail_dsn_param_empty())
}
val logMessage = if (!member.isNullOrEmpty()) zMessages.zdevops_deleting_ds_member(member, dsn, zosConnection.host, zosConnection.zosmfPort)
else zMessages.zdevops_deleting_ds(dsn, zosConnection.host, zosConnection.zosmfPort)
val dsnMemberPattern = Regex("[\\w#\[email protected]]{1,}\\([\\w#\$@]+\\)") //means it's a PDS member
var member: String? = null
if (dsnWithMemName.contains(dsnMemberPattern)) {
member = dsnWithMemName.substringAfter('(').substringBefore(')')
}
val logMessage = if (!member.isNullOrEmpty()) zMessages.zdevops_deleting_ds_member(dsnWithMemName, zosConnection.host, zosConnection.zosmfPort)
else zMessages.zdevops_deleting_ds(dsnWithMemName, zosConnection.host, zosConnection.zosmfPort)
listener.logger.println(logMessage)
try {
if (!member.isNullOrEmpty()) {
isMemberNameValid(member)
ZosDsn(zosConnection).deleteDsn(dsn, member)
ZosDsn(zosConnection).deleteDsn(dsnWithMemName, member)
} else {
ZosDsn(zosConnection).deleteDsn(dsn)
ZosDsn(zosConnection).deleteDsn(dsnWithMemName)
}
listener.logger.println(successMessage)
} catch (e: InvalidNameException) {
throw e
} catch (doesNotExistEx: Exception) {
if(failOnNotExist) {
throw doesNotExistEx
Expand All @@ -97,6 +108,6 @@ fun deleteDatasetOrMember(dsn: String, member: String?, zosConnection: ZOSConnec
*/
private fun isMemberNameValid(member: String) {
if (member.length > 8 || member.isEmpty())
throw Exception(zMessages.zdevops_member_name_invalid())
throw InvalidNameException(zMessages.zdevops_member_name_invalid())
}

2 changes: 1 addition & 1 deletion src/main/resources/org/zowe/zdevops/Messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ zdevops_declarative_writing_file_fail=Cannot write to Unix file {0}

zdevops_deleting_ds_fail=Cannot perform deletion
zdevops_deleting_ds_fail_dsn_param_empty=Unable to delete: no dsn keyword present
zdevops_deleting_ds_member=Deleting member {0} from dataset "{1}" with connection {2}:{3}
zdevops_deleting_ds_member=Deleting member {0} with connection {1}:{2}
zdevops_deleting_ds=Deleting dataset {0} with connection {1}:{2}
zdevops_deleting_ds_by_mask=Deleting datasets that match the mask "{0}"
zdevops_deleting_ds_fail_no_matching_mask=No data sets matching the mask were found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@
<form:entry field="connectionName" title="${%zdevops.classic.connection.title}">
<form:select/>
</form:entry>
<form:entry field="dsn" title="${%zdevops.classic.dsn.title}">
<form:entry field="dsn" title="${%zdevops.classic.dsn.title}" description="${%zdevops.classic.dsn.desc}">
<form:textbox/>
</form:entry>
<form:entry field="member" title="${%zdevops.classic.member.title}">
<form:textbox/>
</form:entry>
<form:entry field="failOnNotExist" title="failOnNotExist?" description="If the dataset does not exists and the option is enabled, execution will halt.">
<form:entry field="failOnNotExist" title="${%zdevops.classic.failOnNotExist.title}">
<form:checkbox/>
</form:entry>
</jelly:jelly>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
zdevops.classic.connection.title=z/OS connection
zdevops.classic.dsn.title=Dataset
zdevops.classic.member.title=Member (optional)
zdevops.classic.dsn.desc=Specify a dataset as HLQ.DSNAME or a member name as HLQ.DSNAME(MEMNAME)
zdevops.classic.failOnNotExist.title=Fail the step if the entity does not exist
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class DeleteDatasetStepSpec : ShouldSpec({
)

val deleteDatasetStep = spyk(
DeleteDatasetStep("test", "TEST.IJMP.DATASET1", member = null)
DeleteDatasetStep("test", "TEST.IJMP.DATASET1")
)
deleteDatasetStep.perform(
build,
Expand Down Expand Up @@ -130,7 +130,7 @@ class DeleteDatasetStepSpec : ShouldSpec({
)

val deleteDatasetStep = spyk(
DeleteDatasetStep("test", "TEST.IJMP.DATASET1", member = null, failOnNotExist = false)
DeleteDatasetStep("test", "TEST.IJMP.DATASET1", failOnNotExist = false)
)
deleteDatasetStep.perform(
build,
Expand All @@ -152,11 +152,5 @@ class DeleteDatasetStepSpec : ShouldSpec({
descriptor.doCheckDsn("") shouldBe FormValidation.error(Messages.zdevops_value_must_not_be_empty_validation())
descriptor.doCheckDsn("MY_DATASET") shouldBe FormValidation.error(Messages.zdevops_dataset_name_is_invalid_validation())
}

should("validate member name") {
descriptor.doCheckMember("") shouldBe FormValidation.ok()
descriptor.doCheckMember("@MY_DS") shouldBe FormValidation.warning(Messages.zdevops_member_name_is_invalid_validation())
descriptor.doCheckMember("DSNAME") shouldBe FormValidation.ok()
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ class DeleteDatasetDeclarativeSpec : ShouldSpec({
val deleteDatasetDecl = spyk(
DeleteDatasetDeclarative()
)
deleteDatasetDecl.setDsn("test")
deleteDatasetDecl.setMember("test")
deleteDatasetDecl.setDsn("test(test)")
deleteDatasetDecl.perform(
run,
workspace,
Expand Down Expand Up @@ -210,8 +209,7 @@ class DeleteDatasetDeclarativeSpec : ShouldSpec({
val deleteDatasetDecl = spyk(
DeleteDatasetDeclarative()
)
deleteDatasetDecl.setDsn("test")
deleteDatasetDecl.setMember("testlongmembername")
deleteDatasetDecl.setDsn("test(testlongmembername)")
runCatching {
deleteDatasetDecl.perform(
run,
Expand Down