Skip to content

Commit

Permalink
Merge branch 'feature/IJMP-1748-operations-rework' into 'release/v2.1.0'
Browse files Browse the repository at this point in the history
IJMP-1748 Requesters workaround

See merge request ijmp/for-mainframe!601
  • Loading branch information
Uladzislau Kalesnikau committed Nov 7, 2024
2 parents 9774d13 + 1ef06b7 commit 6a3e55a
Show file tree
Hide file tree
Showing 35 changed files with 959 additions and 185 deletions.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/eu/ibagroup/formainframe/dataops/Operation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
package eu.ibagroup.formainframe.dataops

/**
* Interface to describe an operation in plugin.
* @param Result result that should be returned after operation execution.
* Interface to describe an operation in the plugin
* @property resultClass the result class of the result that should be returned after an operation execution
*/
interface Operation<Result> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ abstract class MFRemoteAttributesServiceBase<Connection : ConnectionConfigBase,
}

/**
* Get or create virtual file by the provided attributes. Also sends ATTRIBUTES_CHANGED topic message for the created file, triggering "onCreate" event
* Get or create virtual file by the provided attributes.
* Also sends ATTRIBUTES_CHANGED topic message for the created file, triggering "onCreate" event
* @param attributes the attributes to get or create the virtual file by
*/
override fun getOrCreateVirtualFile(attributes: Attributes): MFVirtualFile {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ package eu.ibagroup.formainframe.dataops.attributes

import eu.ibagroup.formainframe.config.connect.ConnectionConfig
import eu.ibagroup.formainframe.config.ws.DSMask
import eu.ibagroup.formainframe.utils.nullIfBlank

/**
* Information object with query mask and connection configuration inside
* to send request for a list of datasets to zosmf.
* to send request for a list of datasets to z/OSMF.
* @param connectionConfig connection configuration to specify the system to work with.
* @param queryMask datasets mask.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ data class RemoteDatasetAttributes(
* @return cloned object
*/
override fun clone(): RemoteDatasetAttributes {
return RemoteDatasetAttributes(datasetInfo.clone(), url, requesters.map {
MaskedRequester(it.connectionConfig, it.queryMask)
}.toMutableList())
return RemoteDatasetAttributes(
datasetInfo.clone(),
url,
requesters.map { MaskedRequester(it.connectionConfig, it.queryMask) }.toMutableList()
)
}

override val name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ data class RemoteJobAttributes(
*/
override fun clone(): FileAttributes {
return RemoteJobAttributes(
jobInfo.clone(), url, requesters.map {
JobsRequester(it.connectionConfig, it.jobsFilter)
}.toMutableList()
jobInfo.clone(),
url,
requesters.map { JobsRequester(it.connectionConfig, it.jobsFilter) }
.toMutableList()
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ data class RemoteUssAttributes(
fileMode?.all
}
return mode == FileModeValue.WRITE.mode
|| mode == FileModeValue.WRITE_EXECUTE.mode
|| mode == FileModeValue.READ_WRITE.mode
|| mode == FileModeValue.READ_WRITE_EXECUTE.mode
|| mode == FileModeValue.WRITE_EXECUTE.mode
|| mode == FileModeValue.READ_WRITE.mode
|| mode == FileModeValue.READ_WRITE_EXECUTE.mode
}

val isReadable: Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020 IBA Group.
* Copyright (c) 2020-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
Expand All @@ -12,11 +12,14 @@
* Zowe Community
*/

package eu.ibagroup.formainframe.dataops.attributes;
package eu.ibagroup.formainframe.dataops.attributes

import eu.ibagroup.formainframe.config.connect.ConnectionConfigBase

/** Interface that is necessary to implement requests to z/OSMF for specific entity (USS files, datasets, jobs etc.) */
/**
* Interface that is necessary to implement requests to z/OSMF for specific entity (USS files, datasets, jobs etc.)
* @property connectionConfig the related connection config
*/
interface Requester<Connection : ConnectionConfigBase> {
val connectionConfig: Connection
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,17 @@ class UssFileFetchProvider(
.execute()

if (response.isSuccessful) {
attributes = response.body()?.items?.filter {
it.name != UPPER_DIR_NAME
}?.map {
RemoteUssAttributes(
rootPath = query.request.path,
ussFile = it,
url = query.connectionConfig.url,
connectionConfig = query.connectionConfig
)
}
attributes = response.body()
?.items
?.filter { it.name != UPPER_DIR_NAME }
?.map {
RemoteUssAttributes(
rootPath = query.request.path,
ussFile = it,
url = query.connectionConfig.url,
connectionConfig = query.connectionConfig
)
}
log.info("${query.request} returned ${attributes?.size ?: 0} entities")
log.debug {
attributes?.joinToString("\n") ?: ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,11 @@ import eu.ibagroup.formainframe.analytics.AnalyticsService
import eu.ibagroup.formainframe.analytics.events.FileAction
import eu.ibagroup.formainframe.analytics.events.FileEvent
import eu.ibagroup.formainframe.api.api
import eu.ibagroup.formainframe.config.connect.ConnectionConfig
import eu.ibagroup.formainframe.config.connect.authToken
import eu.ibagroup.formainframe.dataops.DataOpsManager
import eu.ibagroup.formainframe.dataops.UnitOperation
import eu.ibagroup.formainframe.dataops.attributes.FileAttributes
import eu.ibagroup.formainframe.dataops.attributes.RemoteDatasetAttributes
import eu.ibagroup.formainframe.dataops.attributes.RemoteMemberAttributes
import eu.ibagroup.formainframe.dataops.attributes.RemoteUssAttributes
import eu.ibagroup.formainframe.dataops.attributes.getLibraryAttributes
import eu.ibagroup.formainframe.dataops.attributes.*
import eu.ibagroup.formainframe.dataops.exceptions.CallException
import eu.ibagroup.formainframe.utils.cancelByIndicator
import eu.ibagroup.formainframe.utils.findAnyNullable
Expand All @@ -36,15 +33,60 @@ import eu.ibagroup.formainframe.utils.runWriteActionInEdt
import org.zowe.kotlinsdk.DataAPI
import org.zowe.kotlinsdk.FilePath
import org.zowe.kotlinsdk.XIBMOption
import retrofit2.Call

class DeleteRunnerFactory : OperationRunnerFactory {
override fun buildComponent(dataOpsManager: DataOpsManager): OperationRunner<*, *> {
return DeleteOperationRunner(dataOpsManager)
}
}

class DeleteOperationRunner(private val dataOpsManager: DataOpsManager) :
OperationRunner<DeleteOperation, Unit> {
/**
* Send delete operation call and delete the respective element from the virtual file system
* @param operation the delete operation instance
* @param opRunner the operation runner
* @param progressIndicator the progress indicator to cancel the operation by on the call end
* @param requesters the requesters to perform the operation for
* @param deleteOperationCallBuilder the operation call builder to build the call
* @param exceptionMsg the exception message to put in the [CallException] if the operation was not successful
*/
private fun processDeleteForRequesters(
operation: DeleteOperation,
opRunner: DeleteOperationRunner,
progressIndicator: ProgressIndicator,
requesters: List<Requester<ConnectionConfig>>,
deleteOperationCallBuilder: (ConnectionConfig) -> Call<Void>,
exceptionMsg: String = "Cannot delete the element"
) {
var throwable: Throwable? = null
requesters
.stream()
.map { requester ->
try {
progressIndicator.checkCanceled()
val response = deleteOperationCallBuilder(requester.connectionConfig)
.cancelByIndicator(progressIndicator)
.execute()
if (response.isSuccessful) {
runWriteActionInEdt { operation.file.delete(opRunner) }
true
} else {
throwable = CallException(response, exceptionMsg)
false
}
} catch (t: Throwable) {
throwable = t
false
}
}
.filter { it }
.findAnyNullable()
?: throw (throwable ?: Throwable("Unknown"))
}

class DeleteOperationRunner(
private val dataOpsManager: DataOpsManager
) : OperationRunner<DeleteOperation, Unit> {
override val operationClass = DeleteOperation::class.java
override val log = log<DeleteOperationRunner>()

Expand All @@ -59,97 +101,75 @@ class DeleteOperationRunner(private val dataOpsManager: DataOpsManager) :
operation: DeleteOperation,
progressIndicator: ProgressIndicator
) {
when (val attr = operation.attributes) {
is RemoteDatasetAttributes -> {
AnalyticsService.getService().trackAnalyticsEvent(FileEvent(attr, FileAction.DELETE))
val attr = operation.attributes
AnalyticsService.getService().trackAnalyticsEvent(FileEvent(attr, FileAction.DELETE))

when (attr) {
is RemoteDatasetAttributes -> {
if (operation.file.children != null) {
operation.file.children.forEach { it.isWritable = false }
} else {
operation.file.isWritable = false
}
var throwable: Throwable? = null
attr.requesters.stream().map {
try {
progressIndicator.checkCanceled()
val response = api<DataAPI>(it.connectionConfig).deleteDataset(
authorizationToken = it.connectionConfig.authToken,
datasetName = attr.name
).cancelByIndicator(progressIndicator).execute()
if (response.isSuccessful) {
runWriteActionInEdt { operation.file.delete(this@DeleteOperationRunner) }
true
} else {
throwable = CallException(response, "Cannot delete data set")
false
}
} catch (t: Throwable) {
throwable = t
false
}
}.filter { it }.findAnyNullable() ?: throw (throwable ?: Throwable("Unknown"))
val deleteOperationCallBuilder = { connectionConfig: ConnectionConfig ->
api<DataAPI>(connectionConfig).deleteDataset(
authorizationToken = connectionConfig.authToken,
datasetName = attr.name
)
}
processDeleteForRequesters(
operation,
this,
progressIndicator,
attr.requesters,
deleteOperationCallBuilder,
"Cannot delete data set"
)
}

is RemoteMemberAttributes -> {
AnalyticsService.getService().trackAnalyticsEvent(FileEvent(attr, FileAction.DELETE))

operation.file.isWritable = false
val libraryAttributes = attr.getLibraryAttributes(dataOpsManager)
if (libraryAttributes != null) {
var throwable: Throwable? = null
libraryAttributes.requesters.stream().map {
try {
progressIndicator.checkCanceled()
val response = api<DataAPI>(it.connectionConfig).deleteDatasetMember(
authorizationToken = it.connectionConfig.authToken,
datasetName = libraryAttributes.name,
memberName = attr.name
).cancelByIndicator(progressIndicator).execute()
if (response.isSuccessful) {
runWriteActionInEdt { operation.file.delete(this@DeleteOperationRunner) }
true
} else {
throwable = CallException(response, "Cannot delete data set member")
false
}
} catch (t: Throwable) {
throwable = t
false
}
}.filter { it }.findAnyNullable() ?: throw (throwable ?: Throwable("Unknown"))
val deleteOperationCallBuilder = { connectionConfig: ConnectionConfig ->
api<DataAPI>(connectionConfig).deleteDatasetMember(
authorizationToken = connectionConfig.authToken,
datasetName = libraryAttributes.name,
memberName = attr.name
)
}
processDeleteForRequesters(
operation,
this,
progressIndicator,
libraryAttributes.requesters,
deleteOperationCallBuilder,
"Cannot delete data set member"
)
}
}

is RemoteUssAttributes -> {
AnalyticsService.getService().trackAnalyticsEvent(FileEvent(attr, FileAction.DELETE))

if (operation.file.isDirectory) {
operation.file.children.forEach { it.isWritable = false }
} else {
operation.file.isWritable = false
}
var throwable: Throwable? = null
attr.requesters.stream().map {
try {
progressIndicator.checkCanceled()
val response = api<DataAPI>(it.connectionConfig).deleteUssFile(
authorizationToken = it.connectionConfig.authToken,
filePath = FilePath(attr.path),
xIBMOption = XIBMOption.RECURSIVE
).cancelByIndicator(progressIndicator).execute()
if (response.isSuccessful) {
// TODO: clarify issue with removing from MF Virtual file system
// runWriteActionInEdt { operation.file.delete(this@DeleteOperationRunner) }
true
} else {
throwable = CallException(response, "Cannot delete USS File/Directory")
false
}
} catch (t: Throwable) {
throwable = t
false
}
}.filter { it }.findAnyNullable() ?: throw (throwable ?: Throwable("Unknown"))
val deleteOperationCallBuilder = { connectionConfig: ConnectionConfig ->
api<DataAPI>(connectionConfig).deleteUssFile(
authorizationToken = connectionConfig.authToken,
filePath = FilePath(attr.path),
xIBMOption = XIBMOption.RECURSIVE
)
}
processDeleteForRequesters(
operation,
this,
progressIndicator,
attr.requesters,
deleteOperationCallBuilder,
"Cannot delete USS File/Directory"
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import eu.ibagroup.formainframe.dataops.exceptions.CallException
import eu.ibagroup.formainframe.dataops.operations.DeleteOperation
import eu.ibagroup.formainframe.dataops.operations.OperationRunner
import eu.ibagroup.formainframe.dataops.operations.OperationRunnerFactory
import eu.ibagroup.formainframe.utils.*
import eu.ibagroup.formainframe.utils.applyIfNotNull
import eu.ibagroup.formainframe.utils.cancelByIndicator
import eu.ibagroup.formainframe.utils.log
import eu.ibagroup.formainframe.utils.setUssFileTag
import eu.ibagroup.formainframe.vfs.MFVirtualFile
import org.zowe.kotlinsdk.DataAPI
import org.zowe.kotlinsdk.FilePath
Expand Down Expand Up @@ -54,15 +57,16 @@ class CrossSystemMemberOrUssFileOrSequentialToUssDirMover(val dataOpsManager: Da
* @see OperationRunner.canRun
*/
override fun canRun(operation: MoveCopyOperation): Boolean {
return !operation.source.isDirectory &&
operation.destination.isDirectory &&
return !operation.source.isDirectory
&& operation.destination.isDirectory
&&
(operation.sourceAttributes is RemoteMemberAttributes
|| operation.sourceAttributes is RemoteUssAttributes
|| operation.sourceAttributes is RemoteDatasetAttributes) &&
operation.destinationAttributes is RemoteUssAttributes &&
operation.source is MFVirtualFile &&
operation.destination is MFVirtualFile &&
operation.commonUrls(dataOpsManager).isEmpty()
|| operation.sourceAttributes is RemoteUssAttributes
|| operation.sourceAttributes is RemoteDatasetAttributes)
&& operation.destinationAttributes is RemoteUssAttributes
&& operation.source is MFVirtualFile
&& operation.destination is MFVirtualFile
&& operation.commonUrls(dataOpsManager).isEmpty()
}

override val log = log<CrossSystemMemberOrUssFileOrSequentialToUssDirMover>()
Expand Down
Loading

0 comments on commit 6a3e55a

Please sign in to comment.