Skip to content

Commit

Permalink
Merge pull request #360 from iRevive/sdk-trace/status-data
Browse files Browse the repository at this point in the history
trace sdk: add `StatusData`
  • Loading branch information
iRevive committed Nov 15, 2023
2 parents edccb0d + c1359d6 commit 98ae9fc
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2023 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.typelevel.otel4s.sdk.trace.data

import cats.Hash
import cats.Show
import cats.syntax.show._
import org.typelevel.otel4s.trace.Status

/** Defines the status of a span by providing a standard status in conjunction
* with an optional description message.
*
* @param status
* status code
*
* @see
* [[https://opentelemetry.io/docs/specs/otel/trace/api/#set-status]]
*/
sealed abstract class StatusData(val status: Status) {

/** The description of this status for human consumption.
*/
def description: Option[String]

override final def hashCode(): Int =
Hash[StatusData].hash(this)

override final def equals(obj: Any): Boolean =
obj match {
case other: StatusData => Hash[StatusData].eqv(this, other)
case _ => false
}

override final def toString: String =
Show[StatusData].show(this)
}

object StatusData {

/** Indicates the successfully completed operation, validated by an
* application developer or operator.
*/
case object Ok extends StatusData(Status.Ok) {
def description: Option[String] = None
}

/** The default state.
*/
case object Unset extends StatusData(Status.Unset) {
def description: Option[String] = None
}

/** Indicates an occurred error.
*/
final case class Error(description: Option[String])
extends StatusData(Status.Error)

/** Returns [[StatusData]] for the given `status`.
*/
def apply(status: Status): StatusData =
status match {
case Status.Ok => Ok
case Status.Unset => Unset
case Status.Error => Error(None)
}

/** Creates [[StatusData]] using the given `status` and `description`.
*
* @param status
* the status of the [[StatusData]]
*
* @param description
* the description of the [[StatusData]]. Effective only for the
* [[org.typelevel.otel4s.trace.Status.Error Status.Error]].
*/
def apply(status: Status, description: String): StatusData =
status match {
case Status.Ok => Ok
case Status.Unset => Unset
case Status.Error => Error(Option.when(description.nonEmpty)(description))
}

implicit val statusDataHash: Hash[StatusData] =
Hash.by(a => (a.status, a.description))

implicit val statusDataShow: Show[StatusData] = Show.show { data =>
data.description match {
case Some(d) => show"StatusData{status=${data.status}, description=$d}"
case None => show"StatusData{status=${data.status}}"
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2023 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.typelevel.otel4s.sdk.trace.data

import cats.Show
import cats.kernel.laws.discipline.HashTests
import munit.DisciplineSuite
import org.scalacheck.Arbitrary
import org.scalacheck.Cogen
import org.scalacheck.Gen
import org.scalacheck.Prop
import org.typelevel.otel4s.trace.Status

class StatusDataSuite extends DisciplineSuite {

private val statusGen: Gen[Status] =
Gen.oneOf(Status.Ok, Status.Error, Status.Unset)

private val statusDataGen: Gen[StatusData] =
for {
description <- Gen.alphaNumStr
data <- Gen.oneOf(
StatusData.Ok,
StatusData.Unset,
StatusData.Error(Some(description))
)
} yield data

private implicit val statusDataArbitrary: Arbitrary[StatusData] =
Arbitrary(statusDataGen)

private implicit val statusCogen: Cogen[Status] =
Cogen[String].contramap(_.toString)

private implicit val statusDataCogen: Cogen[StatusData] =
Cogen[(Status, Option[String])].contramap(s => (s.status, s.description))

checkAll("StatusData.HashLaws", HashTests[StatusData].hash)

test("Show[StatusData]") {
Prop.forAll(statusDataGen) { data =>
val expected = data match {
case StatusData.Ok => "StatusData{status=Ok}"
case StatusData.Unset => "StatusData{status=Unset}"
case StatusData.Error(None) => "StatusData{status=Error}"
case StatusData.Error(Some(description)) =>
s"StatusData{status=Error, description=$description}"
}

assertEquals(Show[StatusData].show(data), expected)
}
}

test("StatusData.Ok") {
assertEquals(StatusData.Ok.status, Status.Ok)
assertEquals(StatusData.Ok.description, None)
}

test("StatusData.Unset") {
assertEquals(StatusData.Unset.status, Status.Unset)
assertEquals(StatusData.Unset.description, None)
}

test("StatusData.Error") {
Prop.forAll(Gen.alphaNumStr) { description =>
val desc = Option.when(description.nonEmpty)(description)
val error = StatusData.Error(desc)

assertEquals(error.status, Status.Error)
assertEquals(error.description, desc)
}
}

test("create StatusData from a given status") {
Prop.forAll(statusGen) { status =>
val expected = status match {
case Status.Ok => StatusData.Ok
case Status.Error => StatusData.Error(None)
case Status.Unset => StatusData.Unset
}

assertEquals(StatusData(status), expected)
assertEquals(StatusData(status, ""), expected)
}
}

}

0 comments on commit 98ae9fc

Please sign in to comment.