diff --git a/engine/src/test/scala/cromwell/webservice/MetadataBuilderActorSpec.scala b/engine/src/test/scala/cromwell/webservice/MetadataBuilderActorSpec.scala index ac7966f1da7..a520faee1f2 100644 --- a/engine/src/test/scala/cromwell/webservice/MetadataBuilderActorSpec.scala +++ b/engine/src/test/scala/cromwell/webservice/MetadataBuilderActorSpec.scala @@ -227,6 +227,36 @@ class MetadataBuilderActorSpec extends TestKitSuite with AsyncFlatSpecLike with ) } + + it should "use reverse date ordering (oldest first) for event start and stop values" in { + val eventBuilderList = List( + ("start", "1990-12-20T12:30:00.000Z", OffsetDateTime.now), + ("start", "1990-12-20T12:30:01.000Z", OffsetDateTime.now.plusSeconds(1)), + ("end", "2018-06-02T12:30:00.000Z", OffsetDateTime.now.plusSeconds(2)), + ("end", "2018-06-02T12:30:01.000Z", OffsetDateTime.now.plusSeconds(3)), + ) + val workflowId = WorkflowId.randomId() + val expectedRes = + s""""calls": { + | "fqn": [{ + | "attempt": 1, + | "end": "2018-06-02T12:30:00.000Z", + | "start": "1990-12-20T12:30:00.000Z", + | "shardIndex": -1 + | }] + | }, + | "id": "$workflowId"""".stripMargin + + assertMetadataKeyStructure( + eventList = eventBuilderList, + expectedJson = expectedRes, + workflow = workflowId, + eventMaker = makeCallEvent, + metadataBuilderActorName = "mba-start-end-values", + ) + } + + it should "build JSON object structure from dotted key syntax" in { val eventBuilderList = List( ("a:b:c", "abc", OffsetDateTime.now), diff --git a/services/src/main/scala/cromwell/services/metadata/impl/builder/MetadataComponent.scala b/services/src/main/scala/cromwell/services/metadata/impl/builder/MetadataComponent.scala index 8f2697fe26a..428feafe1a1 100644 --- a/services/src/main/scala/cromwell/services/metadata/impl/builder/MetadataComponent.scala +++ b/services/src/main/scala/cromwell/services/metadata/impl/builder/MetadataComponent.scala @@ -11,6 +11,7 @@ import spray.json._ import scala.collection.immutable.TreeMap import scala.language.postfixOps import scala.util.{Random, Try} +import java.time.Instant object MetadataComponent { implicit val MetadataComponentMonoid: Monoid[MetadataComponent] = new Monoid[MetadataComponent] { @@ -88,8 +89,12 @@ object MetadataComponent { } private def customOrdering(event: MetadataEvent): Option[Ordering[MetadataPrimitive]] = event match { - case MetadataEvent(MetadataKey(_, Some(_), key), _, _) if key == CallMetadataKeys.ExecutionStatus => Option(MetadataPrimitive.ExecutionStatusOrdering) - case MetadataEvent(MetadataKey(_, None, key), _, _) if key == WorkflowMetadataKeys.Status => Option(MetadataPrimitive.WorkflowStateOrdering) + case MetadataEvent(MetadataKey(_, Some(_), key), _, _) + if key == CallMetadataKeys.ExecutionStatus => Option(MetadataPrimitive.ExecutionStatusOrdering) + case MetadataEvent(MetadataKey(_, _, key), _, _) + if key == CallMetadataKeys.Start || key == CallMetadataKeys.End => Option(MetadataPrimitive.TimestampOrdering) + case MetadataEvent(MetadataKey(_, None, key), _, _) + if key == WorkflowMetadataKeys.Status => Option(MetadataPrimitive.WorkflowStateOrdering) case _ => None } @@ -151,6 +156,10 @@ object MetadataPrimitive { val WorkflowStateOrdering: Ordering[MetadataPrimitive] = Ordering.by { primitive: MetadataPrimitive => WorkflowState.withName(primitive.v.value) } + + val TimestampOrdering: Ordering[MetadataPrimitive] = Ordering.by { primitive: MetadataPrimitive => + Instant.parse(primitive.v.value) + }.reverse } case class MetadataPrimitive(v: MetadataValue, customOrdering: Option[Ordering[MetadataPrimitive]] = None) extends MetadataComponent