From b566bf0a1cd88ab1a99e9ba65e98cc2a93287f2e Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Tue, 9 Jul 2019 00:16:54 +0900 Subject: [PATCH 1/8] PostServiceCheckResponse -> StatusResponse --- src/main/scala/scaladog/Client.scala | 6 +++--- src/main/scala/scaladog/PostServiceCheckResponse.scala | 3 --- src/main/scala/scaladog/StatusResponse.scala | 3 +++ src/test/scala/scaladog/ClientImplTest.scala | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 src/main/scala/scaladog/PostServiceCheckResponse.scala create mode 100644 src/main/scala/scaladog/StatusResponse.scala diff --git a/src/main/scala/scaladog/Client.scala b/src/main/scala/scaladog/Client.scala index 03b7a11..10084d7 100644 --- a/src/main/scala/scaladog/Client.scala +++ b/src/main/scala/scaladog/Client.scala @@ -14,7 +14,7 @@ trait Client { timestamp: Instant = Instant.now(), message: String = "", tags: Iterable[(String, String)] = Iterable.empty - ): PostServiceCheckResponse + ): StatusResponse } private[scaladog] class ClientImpl( @@ -45,7 +45,7 @@ private[scaladog] class ClientImpl( timestamp: Instant = Instant.now(), message: String = "", tags: Iterable[(String, String)] = Iterable.empty - ): PostServiceCheckResponse = { + ): StatusResponse = { val bodyJson = ujson.Obj( "check" -> check, "host_name" -> hostName, @@ -63,7 +63,7 @@ private[scaladog] class ClientImpl( ) throwErrorOr(response) { res => - PostServiceCheckResponse(ujson.read(res.text).obj("status").str) + StatusResponse(ujson.read(res.text).obj("status").str) } } diff --git a/src/main/scala/scaladog/PostServiceCheckResponse.scala b/src/main/scala/scaladog/PostServiceCheckResponse.scala deleted file mode 100644 index 73d5337..0000000 --- a/src/main/scala/scaladog/PostServiceCheckResponse.scala +++ /dev/null @@ -1,3 +0,0 @@ -package scaladog - -case class PostServiceCheckResponse(status: String) diff --git a/src/main/scala/scaladog/StatusResponse.scala b/src/main/scala/scaladog/StatusResponse.scala new file mode 100644 index 0000000..bed39d3 --- /dev/null +++ b/src/main/scala/scaladog/StatusResponse.scala @@ -0,0 +1,3 @@ +package scaladog + +case class StatusResponse(status: String) diff --git a/src/test/scala/scaladog/ClientImplTest.scala b/src/test/scala/scaladog/ClientImplTest.scala index e318d19..b8652ef 100644 --- a/src/test/scala/scaladog/ClientImplTest.scala +++ b/src/test/scala/scaladog/ClientImplTest.scala @@ -75,7 +75,7 @@ class ClientImplTest extends FunSpec { ) val actual = client.serviceCheck("app.is_ok", "myhost", ServiceCheckStatus.OK) - val expect = PostServiceCheckResponse("ok") + val expect = StatusResponse("ok") assert(actual == expect) } } From bd52bf9ecc19fe2189ac058c16868aa8805ba3fb Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Tue, 9 Jul 2019 00:22:15 +0900 Subject: [PATCH 2/8] Implement StatusResponse#isOk --- README.md | 2 +- .../test/scala/scaladog/ServiceChecksAPIIntegrationTest.scala | 2 +- src/main/scala/scaladog/StatusResponse.scala | 4 +++- src/test/scala/scaladog/ClientImplTest.scala | 3 +-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f3d246f..1f14c38 100644 --- a/README.md +++ b/README.md @@ -90,5 +90,5 @@ val response = client.serviceCheck( tags = List("env" -> "prod") ) -assert(response.status == "ok") +assert(response.isOk) ``` diff --git a/integrationTests/src/test/scala/scaladog/ServiceChecksAPIIntegrationTest.scala b/integrationTests/src/test/scala/scaladog/ServiceChecksAPIIntegrationTest.scala index 1094e9e..e1df50d 100644 --- a/integrationTests/src/test/scala/scaladog/ServiceChecksAPIIntegrationTest.scala +++ b/integrationTests/src/test/scala/scaladog/ServiceChecksAPIIntegrationTest.scala @@ -13,6 +13,6 @@ class ServiceChecksAPIIntegrationTest extends ClientITSpec { tags = List("env" -> "integration_test") ) - assert(response.status == "ok") + assert(response.isOk) } } diff --git a/src/main/scala/scaladog/StatusResponse.scala b/src/main/scala/scaladog/StatusResponse.scala index bed39d3..4a14713 100644 --- a/src/main/scala/scaladog/StatusResponse.scala +++ b/src/main/scala/scaladog/StatusResponse.scala @@ -1,3 +1,5 @@ package scaladog -case class StatusResponse(status: String) +case class StatusResponse(status: String) { + val isOk: Boolean = status.toLowerCase == "ok" +} diff --git a/src/test/scala/scaladog/ClientImplTest.scala b/src/test/scala/scaladog/ClientImplTest.scala index b8652ef..9bf04a8 100644 --- a/src/test/scala/scaladog/ClientImplTest.scala +++ b/src/test/scala/scaladog/ClientImplTest.scala @@ -75,8 +75,7 @@ class ClientImplTest extends FunSpec { ) val actual = client.serviceCheck("app.is_ok", "myhost", ServiceCheckStatus.OK) - val expect = StatusResponse("ok") - assert(actual == expect) + assert(actual.isOk) } } From 81f40567bfb5f073e9e8634d62548deb54fe6be4 Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Wed, 10 Jul 2019 13:08:07 +0900 Subject: [PATCH 3/8] Support GET /metrics --- .../scaladog/MetricsAPIIntegrationTest.scala | 19 ++++++++ src/main/scala/scaladog/Client.scala | 18 ++++++++ .../scala/scaladog/GetMetricsResponse.scala | 15 +++++++ src/main/scala/scaladog/package.scala | 44 +++++++++++++++++++ src/test/scala/scaladog/ClientImplTest.scala | 27 ++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala create mode 100644 src/main/scala/scaladog/GetMetricsResponse.scala create mode 100644 src/main/scala/scaladog/package.scala diff --git a/integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala b/integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala new file mode 100644 index 0000000..55f2bbe --- /dev/null +++ b/integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala @@ -0,0 +1,19 @@ +package scaladog + +import java.time.Instant +import java.time.temporal.ChronoUnit + +import org.scalatest.Inside + +class MetricsAPIIntegrationTest extends ClientITSpec with Inside { + + test("GET /metrics") { + val from = Instant.now().minus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.SECONDS) + val host = "myhost" + val response = client.getMetrics(from, host) + inside(response) { case GetMetricsResponse(_, actualFrom, actualHost) => + assert(actualFrom == from) + assert(Some(host) == actualHost) + } + } +} diff --git a/src/main/scala/scaladog/Client.scala b/src/main/scala/scaladog/Client.scala index 10084d7..afb1329 100644 --- a/src/main/scala/scaladog/Client.scala +++ b/src/main/scala/scaladog/Client.scala @@ -15,6 +15,7 @@ trait Client { message: String = "", tags: Iterable[(String, String)] = Iterable.empty ): StatusResponse + def getMetrics(from: Instant, host: String = ""): GetMetricsResponse } private[scaladog] class ClientImpl( @@ -67,6 +68,23 @@ private[scaladog] class ClientImpl( } } + def getMetrics(from: Instant, host: String = ""): GetMetricsResponse = { + val response = requester(requests.get) + .apply( + url = s"$baseUrl/metrics", + params = Iterable( + "api_key" -> apiKey, + "application_key" -> appKey, + "from" -> from.getEpochSecond.toString, + "host" -> host + ) + ) + + throwErrorOr(response) { res => + DDPickle.read[GetMetricsResponse](res.text()) + } + } + private def throwErrorOr[T](response: Response)(f: Response => T): T = { if (response.is2xx) { f(response) diff --git a/src/main/scala/scaladog/GetMetricsResponse.scala b/src/main/scala/scaladog/GetMetricsResponse.scala new file mode 100644 index 0000000..61cd58b --- /dev/null +++ b/src/main/scala/scaladog/GetMetricsResponse.scala @@ -0,0 +1,15 @@ +package scaladog + +import java.time.Instant + +import DDPickle._ + +case class GetMetricsResponse( + metrics: Seq[String], + from: Instant, + private val host: Option[String] = None +) + +object GetMetricsResponse { + implicit val reader: Reader[GetMetricsResponse] = macroR +} diff --git a/src/main/scala/scaladog/package.scala b/src/main/scala/scaladog/package.scala new file mode 100644 index 0000000..ef8ae2e --- /dev/null +++ b/src/main/scala/scaladog/package.scala @@ -0,0 +1,44 @@ +import java.time.Instant + +package object scaladog { + + /** + * http://www.lihaoyi.com/upickle/#CustomConfiguration + */ + private[scaladog] object DDPickle extends upickle.AttributeTagged { + private def camelToSnake(s: String) = { + s.split("(?=[A-Z])", -1).map(_.toLowerCase).mkString("_") + } + private def snakeToCamel(s: String) = { + val res = s.split("_", -1).map(x => x(0).toUpper + x.drop(1)).mkString + s(0).toLower + res.drop(1) + } + + override def objectAttributeKeyReadMap(s: CharSequence): String = + snakeToCamel(s.toString) + override def objectAttributeKeyWriteMap(s: CharSequence): String = + camelToSnake(s.toString) + + override def objectTypeKeyReadMap(s: CharSequence): String = + snakeToCamel(s.toString) + override def objectTypeKeyWriteMap(s: CharSequence): String = + camelToSnake(s.toString) + + override implicit def OptionWriter[T: Writer]: Writer[Option[T]] = + implicitly[Writer[T]].comap[Option[T]] { + case None => null.asInstanceOf[T] + case Some(x) => x + } + + override implicit def OptionReader[T: Reader]: Reader[Option[T]] = + implicitly[Reader[T]].mapNulls { + case null => None + case x => Some(x) + } + } + + import DDPickle._ + + private[scaladog] implicit val instantR: Reader[Instant] = + reader[String].map(s => Instant.ofEpochSecond(s.toInt)) +} diff --git a/src/test/scala/scaladog/ClientImplTest.scala b/src/test/scala/scaladog/ClientImplTest.scala index 9bf04a8..c57dc02 100644 --- a/src/test/scala/scaladog/ClientImplTest.scala +++ b/src/test/scala/scaladog/ClientImplTest.scala @@ -1,5 +1,7 @@ package scaladog import java.net.HttpCookie +import java.time.Instant +import java.time.temporal.ChronoUnit import org.scalatest.FunSpec import requests._ @@ -77,6 +79,31 @@ class ClientImplTest extends FunSpec { val actual = client.serviceCheck("app.is_ok", "myhost", ServiceCheckStatus.OK) assert(actual.isOk) } + + it("getMetrics") { + val client = genTestClient( + url = "https://api.datadoghq.com/api/v1/check_run", + statusCode = 200, + """{ + | "metrics": [ + | "system.cpu.guest", + | "system.cpu.idle", + | "system.cpu.iowait" + | ], + | "from": "1559347200", + | "host": "myhost" + |} + """.stripMargin.trim + ) + + val actual = client.getMetrics(Instant.now().minus(1, ChronoUnit.DAYS), "myhost") + val expect = GetMetricsResponse( + Seq("system.cpu.guest", "system.cpu.idle", "system.cpu.iowait"), + Instant.ofEpochSecond(1559347200L), + Some("myhost") + ) + assert(actual == expect) + } } describe("Error handling") { From 787d060a5476308e37f2a3a068acdfa367a97055 Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Tue, 30 Jul 2019 00:38:30 +0900 Subject: [PATCH 4/8] Implement Tag --- src/main/scala/scaladog/Tag.scala | 26 +++++++++++++++ src/test/scala/scaladog/TagTest.scala | 46 +++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/main/scala/scaladog/Tag.scala create mode 100644 src/test/scala/scaladog/TagTest.scala diff --git a/src/main/scala/scaladog/Tag.scala b/src/main/scala/scaladog/Tag.scala new file mode 100644 index 0000000..84de0dd --- /dev/null +++ b/src/main/scala/scaladog/Tag.scala @@ -0,0 +1,26 @@ +package scaladog + +sealed trait Tag { + def asString: String + def asTag: Tag = this +} + +object Tag { + + case class Value(value: String) extends Tag { + val asString: String = value + } + + case class KeyValue(key: String, value: String) extends Tag { + val asString: String = s"$key:$value" + } + + def apply(str: String): Tag = { + val (left, right) = str.span(_ != ':') + if (right.isEmpty) Value(left) + else KeyValue(left, right.drop(1)) + } + + implicit val readwriter: DDPickle.ReadWriter[Tag] = + DDPickle.readwriter[String].bimap(_.asString, apply) +} diff --git a/src/test/scala/scaladog/TagTest.scala b/src/test/scala/scaladog/TagTest.scala new file mode 100644 index 0000000..ce3ee18 --- /dev/null +++ b/src/test/scala/scaladog/TagTest.scala @@ -0,0 +1,46 @@ +package scaladog + +import org.scalatest.FunSpec + +class TagTest extends FunSpec { + + describe("apply") { + it("should bind the empty string to Tag.Value") { + assert(Tag.apply("") == Tag.Value("")) + } + + it("should bind a string without ':' to Tag.Value") { + assert(Tag.apply("the_tag") == Tag.Value("the_tag")) + } + + it("should bind a string with ':' to Tag.KeyValue") { + assert(Tag.apply("key:value") == Tag.KeyValue("key", "value")) + } + + it("should bind a string with multiple ':' to Tag.KeyValue") { + assert(Tag.apply("key:value:foo") == Tag.KeyValue("key", "value:foo")) + } + } + + describe("readwriter") { + it("should read a string without ':'") { + val tag = DDPickle.read[Tag]("\"the_tag\"") + assert(tag == Tag.Value("the_tag")) + } + + it("should read a string with ':'") { + val tag = DDPickle.read[Tag]("\"key:value\"") + assert(tag == Tag.KeyValue("key", "value")) + } + + it("should write a JSON from Tag.Value") { + val json = DDPickle.write(Tag.Value("the_tag").asTag) + assert(json == "\"the_tag\"") + } + + it("should write a JSON from Tag.KeyValue") { + val json = DDPickle.write(Tag.KeyValue("key", "value").asTag) + assert(json == "\"key:value\"") + } + } +} From 315e624db364e2ecacf008028c98b9f64cd36b61 Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Tue, 30 Jul 2019 01:27:42 +0900 Subject: [PATCH 5/8] Implement MetricType --- src/main/scala/scaladog/MetricType.scala | 23 ++++++++++++++++++++ src/test/scala/scaladog/MetricTypeTest.scala | 21 ++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/scala/scaladog/MetricType.scala create mode 100644 src/test/scala/scaladog/MetricTypeTest.scala diff --git a/src/main/scala/scaladog/MetricType.scala b/src/main/scala/scaladog/MetricType.scala new file mode 100644 index 0000000..6501ea9 --- /dev/null +++ b/src/main/scala/scaladog/MetricType.scala @@ -0,0 +1,23 @@ +package scaladog + +sealed abstract class MetricType(val name: String) { + def asMetricType: MetricType = this +} + +object MetricType { + case object Gauge extends MetricType("gauge") + case object Rate extends MetricType("rate") + case object Count extends MetricType("count") + + val all: Set[MetricType] = Set(Gauge, Rate, Count) + + def fromString(str: String): Option[MetricType] = all.find(_.name == str) + + implicit def readwriter: DDPickle.ReadWriter[MetricType] = + DDPickle + .readwriter[String] + .bimap( + _.name, + s => fromString(s).getOrElse(throw new IllegalArgumentException(s"Invalid MetricType: $s")) + ) +} diff --git a/src/test/scala/scaladog/MetricTypeTest.scala b/src/test/scala/scaladog/MetricTypeTest.scala new file mode 100644 index 0000000..eb0e76b --- /dev/null +++ b/src/test/scala/scaladog/MetricTypeTest.scala @@ -0,0 +1,21 @@ +package scaladog + +import org.scalatest.FunSpec + +class MetricTypeTest extends FunSpec { + + describe("readwriter") { + + it("should read a string") { + assert(DDPickle.read[MetricType]("\"rate\"") == MetricType.Rate) + } + + it("should throw an IllegalArgumentException when read an invalid string") { + assertThrows[IllegalArgumentException](DDPickle.read[MetricType]("\"invalid\"") == null) + } + + it("should write JSON from a string") { + assert(DDPickle.write(MetricType.Gauge.asMetricType) == "\"gauge\"") + } + } +} From 75462401a757b2ee07ee696c332b01257e78eee4 Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Tue, 30 Jul 2019 01:40:06 +0900 Subject: [PATCH 6/8] Implement Series --- src/main/scala/scaladog/Series.scala | 22 +++++++++++++ src/test/scala/scaladog/SeriesTest.scala | 41 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/main/scala/scaladog/Series.scala create mode 100644 src/test/scala/scaladog/SeriesTest.scala diff --git a/src/main/scala/scaladog/Series.scala b/src/main/scala/scaladog/Series.scala new file mode 100644 index 0000000..dce2e9a --- /dev/null +++ b/src/main/scala/scaladog/Series.scala @@ -0,0 +1,22 @@ +package scaladog + +import java.time.Instant + +case class Series( + metric: String, + points: Seq[Point], + host: String = "", + tags: Seq[Tag] = Seq.empty, + @upickle.implicits.key("type") metricType: MetricType = MetricType.Gauge +) + +object Series { + implicit val writer: DDPickle.Writer[Series] = DDPickle.macroW +} + +case class Point(timestamp: Instant, value: BigDecimal) + +object Point { + implicit val writer: DDPickle.Writer[Point] = + DDPickle.writer[ujson.Arr].comap(p => ujson.Arr(p.timestamp.getEpochSecond, p.value.toString())) +} diff --git a/src/test/scala/scaladog/SeriesTest.scala b/src/test/scala/scaladog/SeriesTest.scala new file mode 100644 index 0000000..a614a31 --- /dev/null +++ b/src/test/scala/scaladog/SeriesTest.scala @@ -0,0 +1,41 @@ +package scaladog + +import java.time.Instant + +import org.scalatest.FunSpec + +class SeriesTest extends FunSpec { + + describe("writer") { + it("should serialize Series to JSON object") { + val series = Series( + metric = "app.test", + points = Seq( + Point(Instant.ofEpochSecond(1559347200L), 10.5) + ), + host = "myhost", + tags = Seq(Tag("key:value")), + metricType = MetricType.Rate + ) + val actual = DDPickle.write(series, 2) + val expect = + """{ + | "metric": "app.test", + | "points": [ + | [ + | "1559347200", + | "10.5" + | ] + | ], + | "host": "myhost", + | "tags": [ + | "key:value" + | ], + | "type": "rate" + |} + """.stripMargin.trim + + assert(actual == expect) + } + } +} From 25e61a2fa4bb3ba2b2f0d7e892b74e7ffedb63d0 Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Tue, 30 Jul 2019 02:04:56 +0900 Subject: [PATCH 7/8] Support POST /series --- .../scaladog/MetricsAPIIntegrationTest.scala | 17 +++++++++++++++++ src/main/scala/scaladog/Client.scala | 18 ++++++++++++++++++ .../scala/scaladog/PostMetricsRequest.scala | 7 +++++++ src/main/scala/scaladog/StatusResponse.scala | 4 ++++ src/test/scala/scaladog/ClientImplTest.scala | 16 ++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 src/main/scala/scaladog/PostMetricsRequest.scala diff --git a/integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala b/integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala index 55f2bbe..0db9b3c 100644 --- a/integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala +++ b/integrationTests/src/test/scala/scaladog/MetricsAPIIntegrationTest.scala @@ -5,6 +5,8 @@ import java.time.temporal.ChronoUnit import org.scalatest.Inside +import scala.util.Random + class MetricsAPIIntegrationTest extends ClientITSpec with Inside { test("GET /metrics") { @@ -16,4 +18,19 @@ class MetricsAPIIntegrationTest extends ClientITSpec with Inside { assert(Some(host) == actualHost) } } + + test("POST /series") { + val response = client.postMetrics( + Seq( + Series( + metric = "test.metric", + points = Seq(Point(Instant.now(), Random.nextInt(1000))), + host = "myhost", + tags = Seq(Tag("project:scaladog")), + MetricType.Gauge + ) + ) + ) + assert(response.isOk) + } } diff --git a/src/main/scala/scaladog/Client.scala b/src/main/scala/scaladog/Client.scala index afb1329..6ad1db0 100644 --- a/src/main/scala/scaladog/Client.scala +++ b/src/main/scala/scaladog/Client.scala @@ -16,6 +16,7 @@ trait Client { tags: Iterable[(String, String)] = Iterable.empty ): StatusResponse def getMetrics(from: Instant, host: String = ""): GetMetricsResponse + def postMetrics(series: Seq[Series]): StatusResponse } private[scaladog] class ClientImpl( @@ -85,6 +86,23 @@ private[scaladog] class ClientImpl( } } + def postMetrics(series: Seq[Series]): StatusResponse = { + val response = requester(requests.post) + .apply( + url = s"$baseUrl/series", + params = Iterable( + "api_key" -> apiKey, + "application_key" -> appKey + ), + headers = Iterable(Client.jsonHeader), + data = DDPickle.write(PostMetricsRequest(series)) + ) + + throwErrorOr(response) { res => + DDPickle.read[StatusResponse](res.text()) + } + } + private def throwErrorOr[T](response: Response)(f: Response => T): T = { if (response.is2xx) { f(response) diff --git a/src/main/scala/scaladog/PostMetricsRequest.scala b/src/main/scala/scaladog/PostMetricsRequest.scala new file mode 100644 index 0000000..9741991 --- /dev/null +++ b/src/main/scala/scaladog/PostMetricsRequest.scala @@ -0,0 +1,7 @@ +package scaladog + +private[scaladog] case class PostMetricsRequest(series: Seq[Series]) + +private[scaladog] object PostMetricsRequest { + implicit val writer: DDPickle.Writer[PostMetricsRequest] = DDPickle.macroW +} diff --git a/src/main/scala/scaladog/StatusResponse.scala b/src/main/scala/scaladog/StatusResponse.scala index 4a14713..b382ee5 100644 --- a/src/main/scala/scaladog/StatusResponse.scala +++ b/src/main/scala/scaladog/StatusResponse.scala @@ -3,3 +3,7 @@ package scaladog case class StatusResponse(status: String) { val isOk: Boolean = status.toLowerCase == "ok" } + +object StatusResponse { + implicit val reader: DDPickle.Reader[StatusResponse] = DDPickle.macroR +} diff --git a/src/test/scala/scaladog/ClientImplTest.scala b/src/test/scala/scaladog/ClientImplTest.scala index c57dc02..45bcb08 100644 --- a/src/test/scala/scaladog/ClientImplTest.scala +++ b/src/test/scala/scaladog/ClientImplTest.scala @@ -104,6 +104,22 @@ class ClientImplTest extends FunSpec { ) assert(actual == expect) } + + it("postMetrics") { + val client = genTestClient( + url = "https://api.datadoghq.com/api/v1/series", + statusCode = 200, + """{ + | "status":"ok" + |} + """.stripMargin.trim + ) + + val actual = client.postMetrics(Seq.empty) + val expect = StatusResponse("ok") + + assert(actual == expect) + } } describe("Error handling") { From 10058f7428f56ff1398732915aa179f2aee2371b Mon Sep 17 00:00:00 2001 From: NomadBlacky Date: Thu, 1 Aug 2019 00:36:31 +0900 Subject: [PATCH 8/8] Add documents of Metrics API usages --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f14c38..e2641e8 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ assert(client.validate()) ## Examples -### Service Checks +### [Service Checks](https://docs.datadoghq.com/api/?lang=bash#service-checks) #### [Post a check run](https://docs.datadoghq.com/api/?lang=bash#post-a-check-run) @@ -92,3 +92,41 @@ val response = client.serviceCheck( assert(response.isOk) ``` + +### [Metrics](https://docs.datadoghq.com/api/?lang=bash#metrics) + +#### [Get list of active metrics](https://docs.datadoghq.com/api/?lang=bash#get-list-of-active-metrics) + +```scala +import java.time._, temporal._ + +val client = scaladog.Client() + +val from = Instant.now().minus(1, ChronoUnit.DAYS) +val host = "myhost" +val response = client.getMetrics(from, host) + +println(response) // GetMetricsResponse(List(test.metric),2019-07-30T15:22:39Z,Some(myhost)) +``` + +#### [Post timeseries points](https://docs.datadoghq.com/api/?lang=bash#post-timeseries-points) + +```scala +import scaladog._ +import java.time.Instant +import scala.util.Random + +val response = scaladog.Client().postMetrics( + Seq( + Series( + metric = "test.metric", + points = Seq(Point(Instant.now(), Random.nextInt(1000))), + host = "myhost", + tags = Seq(Tag("project:scaladog")), + MetricType.Gauge + ) + ) +) + +assert(response.isOk) +```