-
Notifications
You must be signed in to change notification settings - Fork 359
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
[WX-1468] Implement returnCodes
runtime attribute
#7389
Changes from 12 commits
6e8399b
a183b05
d106db0
f3f9972
5f0e393
4f1cadb
d830f48
5427d6c
2c4d8b3
8e1f888
f2b548c
b731aec
23c6c07
113b0d4
1e689fe
4ca9ca5
3f96f9d
021a42e
dba7c9e
461525c
30a1262
58eb36d
b582eb5
9876bb4
27079d7
1091067
5f16cf1
dde66aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,7 @@ | ||
package cromwell.backend.validation | ||
|
||
import cats.data.Validated.{Invalid, Valid} | ||
import cats.implicits._ | ||
import com.typesafe.config.Config | ||
import cromwell.backend.validation.RuntimeAttributesValidation._ | ||
import common.validation.ErrorOr._ | ||
import wom.RuntimeAttributesKeys | ||
import wom.types._ | ||
|
@@ -23,42 +21,29 @@ import scala.util.Try | |
* `default` a validation with the default value specified by the reference.conf file. | ||
*/ | ||
object ContinueOnReturnCodeValidation { | ||
lazy val instance: RuntimeAttributesValidation[ContinueOnReturnCode] = new ContinueOnReturnCodeValidation | ||
def default(runtimeConfig: Option[Config]): RuntimeAttributesValidation[ContinueOnReturnCode] = | ||
lazy val instance: RuntimeAttributesValidation[ReturnCode] = new ContinueOnReturnCodeValidation | ||
def default(runtimeConfig: Option[Config]): RuntimeAttributesValidation[ReturnCode] = | ||
instance.withDefault(configDefaultWdlValue(runtimeConfig) getOrElse WomInteger(0)) | ||
def configDefaultWdlValue(runtimeConfig: Option[Config]): Option[WomValue] = | ||
instance.configDefaultWomValue(runtimeConfig) | ||
} | ||
|
||
class ContinueOnReturnCodeValidation extends RuntimeAttributesValidation[ContinueOnReturnCode] { | ||
class ContinueOnReturnCodeValidation extends ReturnCodeValidation { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would love to see if we can collapse all the return code stuff into this validation, and have this be the last thing in the code path that's aware of the two different attributes. We could even continue calling it |
||
|
||
override def key: String = RuntimeAttributesKeys.ContinueOnReturnCodeKey | ||
|
||
override def coercion: Set[WomType] = ContinueOnReturnCode.validWdlTypes | ||
|
||
override def validateValue: PartialFunction[WomValue, ErrorOr[ContinueOnReturnCode]] = { | ||
override def validateValue: PartialFunction[WomValue, ErrorOr[ReturnCode]] = { | ||
case WomBoolean(value) => ContinueOnReturnCodeFlag(value).validNel | ||
case WomString(value) if Try(value.toBoolean).isSuccess => ContinueOnReturnCodeFlag(value.toBoolean).validNel | ||
case WomString(value) if Try(value.toInt).isSuccess => ContinueOnReturnCodeSet(Set(value.toInt)).validNel | ||
case WomInteger(value) => ContinueOnReturnCodeSet(Set(value)).validNel | ||
case value @ WomArray(_, seq) => | ||
val errorOrInts: ErrorOr[List[Int]] = (seq.toList map validateInt).sequence[ErrorOr, Int] | ||
errorOrInts match { | ||
case Valid(ints) => ContinueOnReturnCodeSet(ints.toSet).validNel | ||
case Invalid(_) => invalidValueFailure(value) | ||
} | ||
case value => super.validateValue(value) | ||
} | ||
|
||
override def validateExpression: PartialFunction[WomValue, Boolean] = { | ||
case WomBoolean(_) => true | ||
case WomString(value) if Try(value.toInt).isSuccess => true | ||
case WomString(value) if Try(value.toBoolean).isSuccess => true | ||
case WomInteger(_) => true | ||
case WomArray(WomArrayType(WomStringType), elements) => | ||
elements forall { value => | ||
Try(value.valueString.toInt).isSuccess | ||
} | ||
case WomArray(WomArrayType(WomIntegerType), _) => true | ||
case value => super.validateExpression(value) | ||
} | ||
|
||
override protected def missingValueMessage: String = s"Expecting $key" + | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package cromwell.backend.validation | ||
|
||
trait ReturnCode { | ||
|
||
/** | ||
* Returns true if the call is a success based on the return code. | ||
* | ||
* @param returnCode Return code from the process / script. | ||
* @return True if the call is a success. | ||
*/ | ||
def continueFor(returnCode: Int): Boolean = | ||
this match { | ||
case ReturnCodeSet(returnCodes) => returnCodes.contains(returnCode) | ||
} | ||
} | ||
|
||
/** | ||
* Continues only if the call/job return code is found in returnCodes. | ||
* @param returnCodes Inclusive set of return codes that specify a job success. | ||
*/ | ||
case class ReturnCodeSet(returnCodes: Set[Int]) extends ReturnCode { | ||
override def toString = returnCodes match { | ||
case single if single.size == 1 => returnCodes.head.toString | ||
case multiple => s"[${multiple.mkString(",")}]" | ||
} | ||
|
||
/** | ||
* Returns true if the call is a success based on the return code. | ||
* | ||
* @param returnCode Return code from the process / script. | ||
* @return True if the call is a success. | ||
*/ | ||
override def continueFor(returnCode: Int): Boolean = super.continueFor(returnCode) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method looks like a no-op, since it overrides the |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package cromwell.backend.validation | ||
|
||
import cats.data.Validated.{Invalid, Valid} | ||
import cats.implicits.{catsSyntaxValidatedId, toTraverseOps} | ||
import common.validation.ErrorOr.ErrorOr | ||
import cromwell.backend.validation.RuntimeAttributesValidation.validateInt | ||
import wom.types.{WomArrayType, WomIntegerType, WomStringType, WomType} | ||
import wom.values.{WomArray, WomInteger, WomString, WomValue} | ||
|
||
import scala.util.Try | ||
|
||
/** | ||
* Validates the `continueOnReturnCode` and `returnCodes` runtime attributes, since they share the functionality of | ||
* being a Set of Integers. | ||
*/ | ||
trait ReturnCodeValidation extends RuntimeAttributesValidation[ReturnCode] { | ||
override def key: String | ||
override def coercion: Iterable[WomType] | ||
override def validateValue: PartialFunction[WomValue, ErrorOr[ReturnCode]] = { | ||
case WomString(value) if Try(value.toInt).isSuccess => ReturnCodeSet(Set(value.toInt)).validNel | ||
case WomInteger(value) => ReturnCodeSet(Set(value)).validNel | ||
case value @ WomArray(_, seq) => | ||
val errorOrInts: ErrorOr[List[Int]] = (seq.toList map validateInt).sequence[ErrorOr, Int] | ||
errorOrInts match { | ||
case Valid(ints) => ReturnCodeSet(ints.toSet).validNel | ||
case Invalid(_) => invalidValueFailure(value) | ||
} | ||
case value => invalidValueFailure(value) | ||
} | ||
|
||
override def validateExpression: PartialFunction[WomValue, Boolean] = { | ||
case WomString(value) if Try(value.toInt).isSuccess => true | ||
case WomInteger(_) => true | ||
case WomArray(WomArrayType(WomStringType), elements) => | ||
elements forall { value => | ||
Try(value.valueString.toInt).isSuccess | ||
} | ||
case WomArray(WomArrayType(WomIntegerType), _) => true | ||
case _ => false | ||
} | ||
|
||
override def usedInCallCaching: Boolean = true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package cromwell.backend.validation | ||
|
||
import wom.types._ | ||
|
||
object ReturnCodes { | ||
val validWdlTypes = Set[WomType](WomArrayType(WomIntegerType), WomStringType, WomIntegerType) | ||
} | ||
|
||
/** | ||
* Decides if a call/job continues upon a specific return code. | ||
*/ | ||
sealed trait ReturnCodes extends ReturnCode { | ||
|
||
/** | ||
* Returns true if the call is a success based on the return code. | ||
* | ||
* @param returnCode Return code from the process / script. | ||
* @return True if the call is a success. | ||
*/ | ||
final override def continueFor(returnCode: Int): Boolean = | ||
this match { | ||
case ReturnCodesString(continue) => continue.equals("*") || returnCode == 0 | ||
case _ => super.continueFor(returnCode) | ||
} | ||
} | ||
|
||
/** | ||
* Continues based on a string, if "*" all return codes continue. | ||
* @param returnCode If "*", all return codes are valid for continuing. | ||
*/ | ||
case class ReturnCodesString(returnCode: String) extends ReturnCodes { | ||
override def toString = returnCode | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package cromwell.backend.validation | ||
|
||
import cats.implicits.catsSyntaxValidatedId | ||
import com.typesafe.config.Config | ||
import common.validation.ErrorOr.ErrorOr | ||
import wom.RuntimeAttributesKeys | ||
import wom.types.WomType | ||
import wom.values.{WomInteger, WomString, WomValue} | ||
|
||
/** | ||
* Validates the "returnCodes" runtime attribute as an Integer, returning the value as an `Int`, or as | ||
* an array of Integers, returning the value as Array[Int], or "*", returning the value as a String. | ||
* | ||
* `default` a hardcoded default WomValue for returnCodes. | ||
* | ||
* `configDefaultWdlValue` returns the value of the attribute as specified by the | ||
* reference.conf file, coerced into a WomValue. | ||
*/ | ||
object ReturnCodesValidation { | ||
lazy val instance: RuntimeAttributesValidation[ReturnCode] = new ReturnCodesValidation | ||
lazy val optional: OptionalRuntimeAttributesValidation[ReturnCode] = instance.optional | ||
def default(runtimeConfig: Option[Config]): RuntimeAttributesValidation[ReturnCode] = | ||
instance.withDefault(configDefaultWdlValue(runtimeConfig) getOrElse WomInteger(0)) | ||
|
||
def configDefaultWdlValue(runtimeConfig: Option[Config]): Option[WomValue] = | ||
instance.configDefaultWomValue(runtimeConfig) | ||
def configDefaultWomValue(config: Option[Config]): Option[WomValue] = instance.configDefaultWomValue(config) | ||
} | ||
|
||
class ReturnCodesValidation extends ReturnCodeValidation { | ||
|
||
override def key: String = RuntimeAttributesKeys.ReturnCodesKey | ||
|
||
override def coercion: Set[WomType] = ReturnCodes.validWdlTypes | ||
|
||
override def validateValue: PartialFunction[WomValue, ErrorOr[ReturnCode]] = { | ||
case WomString(value) if value.equals("*") => | ||
ReturnCodesString(value).validNel | ||
case value => super.validateValue(value) | ||
} | ||
|
||
override def validateExpression: PartialFunction[WomValue, Boolean] = { | ||
case WomString(value) if value.equals("*") => true | ||
case value => super.validateExpression(value) | ||
} | ||
|
||
override protected def missingValueMessage: String = s"Expecting $key" + | ||
" runtime attribute to be either a String '*' or an Array[Int]" | ||
|
||
override def usedInCallCaching: Boolean = true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this is checking whether we have the default value for
returnCodes
and falling back tocontinueOnReturnCode
if we do. What if someone included the default value in their WDL? Can we insert the default after we choose which attribute to use?If not, we could build the default check into
ReturnCode
, so we can call something likereturnCodes.isDefault
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, just for fun, a more idiomatic Scala way to write this is: