Skip to content

Commit

Permalink
Self-service errors: generate error deprecation docs [KVL-1187] (#11539)
Browse files Browse the repository at this point in the history
* Render error deprecation in generated docs

CHANGELOG_BEGIN
CHANGELOG_END

* Build export-error-codes-json-app with Scala 2.13 only

* Generate docs only with Scala 2.13

* Generate daml-on-sql docs only with Scala 2.13

* Split the error codes JSON generator off from the error lib

* Build all docs only with Scala 2.13
  • Loading branch information
fabiotudone-da authored Nov 5, 2021
1 parent 8d48cf6 commit 0823601
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 42 deletions.
13 changes: 7 additions & 6 deletions docs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ load("@os_info//:os_info.bzl", "is_linux", "is_windows")
load("//rules_daml:daml.bzl", "daml_build_test", "daml_compile", "daml_test")
load("@rules_pkg//:pkg.bzl", "pkg_tar")
load("@build_environment//:configuration.bzl", "mvn_version", "sdk_version")
load("@scala_version//:index.bzl", "scala_major_version")

exports_files(
[
Expand Down Expand Up @@ -194,7 +195,7 @@ genrule(
"@sphinx_nix//:bin/sphinx-build",
"@imagemagick_nix//:bin/convert",
] + (["@glibc_locales//:locale-archive"] if is_linux else []),
) if not is_windows else None
) if scala_major_version == "2.13" and not is_windows else None

genrule(
name = "docs-no-pdf",
Expand Down Expand Up @@ -316,7 +317,7 @@ genrule(
"//bazel_tools/sh:mktgz",
"//docs:generate-error-codes-json",
] + (["@glibc_locales//:locale-archive"] if is_linux else []),
) if not is_windows else None
) if scala_major_version == "2.13" and not is_windows else None

genrule(
name = "redirects",
Expand Down Expand Up @@ -383,7 +384,7 @@ genrule(
stamp = 1,
tags = ["pdfdocs"],
tools = ["//bazel_tools/sh:mktgz"],
) if not is_windows else None
) if scala_major_version == "2.13" and not is_windows else None

filegroup(
name = "daml-assistant-iou-setup",
Expand Down Expand Up @@ -660,10 +661,10 @@ genrule(
name = "generate-error-codes-json",
srcs = [],
outs = ["error_codes_export.json"],
cmd = "$(location //ledger/error:export-error-codes-json-app) $(location error_codes_export.json)",
tools = ["//ledger/error:export-error-codes-json-app"],
cmd = "$(location //ledger/error/generator:export-error-codes-json-app) $(location error_codes_export.json)",
tools = ["//ledger/error/generator:export-error-codes-json-app"],
visibility = ["//visibility:public"],
)
) if scala_major_version == "2.13" else None

exports_files([
"source/daml-script/template-root/src/ScriptExample.daml",
Expand Down
4 changes: 4 additions & 0 deletions docs/sphinx_ext/self_service_error_codes_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ def item_to_node(item: Dict[str, Any]) -> nodes.definition_list_item:
node = nodes.definition_list_item()
term_node = text_node(nodes.term, "%s" % (item["code"]))
definition_node = nodes.definition('', text_node(nodes.paragraph, ''))
if item["deprecation"]:
definition_node += build_indented_bold_and_non_bold_node(
bold_text="Deprecated: ",
non_bold_text=item['deprecation'])
if item["explanation"]:
definition_node += build_indented_bold_and_non_bold_node(
bold_text="Explanation: ",
Expand Down
3 changes: 2 additions & 1 deletion ledger/daml-on-sql/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ load("//ledger/ledger-api-test-tool:conformance.bzl", "conformance_test")
load("@os_info//:os_info.bzl", "is_linux", "is_windows")
load("@build_environment//:configuration.bzl", "sdk_version")
load("//daml-lf/language:daml-lf.bzl", "lf_versions_aggregate")
load("@scala_version//:index.bzl", "scala_major_version")

da_scala_library(
name = "daml-on-sql",
Expand Down Expand Up @@ -193,4 +194,4 @@ genrule(
"@sphinx_nix//:bin/sphinx-build",
"//bazel_tools/sh:mktgz",
] + (["@glibc_locales//:locale-archive"] if is_linux else []),
) if not is_windows else None
) if scala_major_version == "2.13" and not is_windows else None
17 changes: 1 addition & 16 deletions ledger/error/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,12 @@ load(
"da_scala_test_suite",
)

da_scala_binary(
name = "export-error-codes-json-app",
srcs = [],
main_class = "com.daml.error.generator.Main",
resources = glob(["src/main/resources/**/*"]),
visibility = ["//visibility:public"],
deps = [
"//ledger/error",
"//ledger/participant-integration-api",
"//ledger/participant-state/kvutils",
],
)

da_scala_library(
name = "error",
srcs = glob(["src/main/scala/**/*.scala"]),
scala_deps = [
"@maven//:org_typelevel_cats_core",
"@maven//:io_spray_spray_json",
"@maven//:io_circe_circe_core",
],
tags = ["maven_coordinates=com.daml:error:__VERSION__"],
versioned_scala_deps = {
Expand All @@ -49,7 +35,6 @@ da_scala_library(
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:io_grpc_grpc_api",
"@maven//:io_grpc_grpc_protobuf",
"@maven//:org_reflections_reflections",
"@maven//:org_slf4j_slf4j_api",
],
)
Expand All @@ -59,7 +44,7 @@ da_scala_library(
srcs = glob(["src/test/utils/**/*.scala"]),
scala_deps = [],
tags = ["maven_coordinates=com.daml:error-test-package:__VERSION__"],
visibility = ["//visibility:private"],
visibility = ["//:__subpackages__"],
deps = [
":error",
"//libs-scala/contextualized-logging",
Expand Down
50 changes: 50 additions & 0 deletions ledger/error/generator/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

load(
"//bazel_tools:scala.bzl",
"da_scala_binary",
"da_scala_test_suite",
)
load("@scala_version//:index.bzl", "scala_major_version")

da_scala_binary(
name = "export-error-codes-json-app",
srcs = glob(["src/main/scala/**/*.scala"]),
main_class = "com.daml.error.generator.Main",
resources = glob(["src/main/resources/**/*"]),
scala_deps = [
"@maven//:io_circe_circe_core",
"@maven//:org_typelevel_cats_core",
],
visibility = ["//visibility:public"],
deps = [
"//ledger/error",
"@maven//:org_reflections_reflections",
],
) if scala_major_version == "2.13" else None

da_scala_test_suite(
name = "export-error-codes-json-tests",
srcs = glob(["src/test/suite/scala/**/*.scala"]),
scala_deps = [
"@maven//:org_scalactic_scalactic",
"@maven//:org_scalatest_scalatest_core",
"@maven//:org_scalatest_scalatest_matchers_core",
"@maven//:org_scalatest_scalatest_shouldmatchers",
],
deps = [
":export-error-codes-json-app",
"//ledger/error",
"//ledger/error:error-test-utils",
"//ledger/test-common",
"//libs-scala/contextualized-logging",
"@maven//:ch_qos_logback_logback_classic",
"@maven//:com_google_api_grpc_proto_google_common_protos",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:io_grpc_grpc_api",
"@maven//:io_grpc_grpc_protobuf",
"@maven//:org_scalatest_scalatest_compatible",
"@maven//:org_slf4j_slf4j_api",
],
) if scala_major_version == "2.13" else None
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
package com.daml.error.generator

import java.lang.reflect.Modifier
import com.daml.error.{ErrorCode, ErrorGroup, Explanation, Resolution}

import com.daml.error.{Deprecation, ErrorCode, ErrorGroup, Explanation, Resolution}
import com.daml.error.generator.ErrorCodeDocumentationGenerator.{
acceptedTypeNames,
deprecatedTypeName,
Expand Down Expand Up @@ -58,7 +59,7 @@ class ErrorCodeDocumentationGenerator(prefixes: Array[String] = Array("com.daml"
.toSeq

private def convertToDocItem(error: ErrorCode): ErrorDocItem = {
val ErrorDocumentationAnnotations(explanation, resolution) =
val ErrorDocumentationAnnotations(deprecation, explanation, resolution) =
getErrorDocumentationAnnotations(error)

ErrorDocItem(
Expand All @@ -67,6 +68,7 @@ class ErrorCodeDocumentationGenerator(prefixes: Array[String] = Array("com.daml"
hierarchicalGrouping = error.parent.groupings.filter(_.docName.nonEmpty),
conveyance = error.errorConveyanceDocString.getOrElse(""),
code = error.id,
deprecation = deprecation.getOrElse(Deprecation("")),
explanation = explanation.getOrElse(Explanation("")),
resolution = resolution.getOrElse(Resolution("")),
)
Expand All @@ -86,6 +88,7 @@ class ErrorCodeDocumentationGenerator(prefixes: Array[String] = Array("com.daml"
any.getClass.getSimpleName.replace("$", "")

private case class ErrorDocumentationAnnotations(
deprecation: Option[Deprecation],
explanation: Option[Explanation],
resolution: Option[Resolution],
)
Expand Down Expand Up @@ -114,6 +117,7 @@ class ErrorCodeDocumentationGenerator(prefixes: Array[String] = Array("com.daml"
}

private case class GetAnnotationsState(
deprecation: Option[Deprecation],
explanation: Option[Explanation],
resolution: Option[Resolution],
)
Expand All @@ -124,6 +128,7 @@ class ErrorCodeDocumentationGenerator(prefixes: Array[String] = Array("com.daml"

def update(
state: GetAnnotationsState,
updatedDeprecation: Option[String] = None,
updatedExplanation: Option[String] = None,
updatedResolution: Option[String] = None,
): GetAnnotationsState = {
Expand All @@ -140,38 +145,52 @@ class ErrorCodeDocumentationGenerator(prefixes: Array[String] = Array("com.daml"
Some(value)
}

val existingDeprecation = state.deprecation
val updatedDeprecationString =
updateString(existingDeprecation.map(_.deprecation), updatedDeprecation, "deprecation")
val existingExplanation = state.explanation
val updatedExplanationString =
updateString(existingExplanation.map(_.explanation), updatedExplanation, "explanation")
val existingResolution = state.resolution
val updatedResolutionString =
updateString(existingResolution.map(_.resolution), updatedResolution, "resolution")
GetAnnotationsState(
updatedDeprecationString.map(Deprecation),
updatedExplanationString.map(Explanation),
updatedResolutionString.map(Resolution),
)
}

val doc = annotations.foldLeft(GetAnnotationsState(None, None)) { case (state, annotation) =>
if (isAnnotation(annotation, deprecatedTypeName))
state
else if (isAnnotation(annotation, explanationTypeName))
update(state, updatedExplanation = Some(parseAnnotationValue(annotation.tree)))
else if (isAnnotation(annotation, resolutionTypeName))
update(state, updatedResolution = Some(parseAnnotationValue(annotation.tree)))
else
sys.error(
s"Unexpected annotation detected (${annotations.map(annotationTypeName)} but the only supported ones are $acceptedTypeNames)."
)
val doc = annotations.foldLeft(GetAnnotationsState(None, None, None)) {
case (state, annotation) =>
if (isAnnotation(annotation, deprecatedTypeName))
update(state, updatedDeprecation = Some(parseDeprecatedAnnotationValue(annotation.tree)))
else if (isAnnotation(annotation, explanationTypeName))
update(state, updatedExplanation = Some(parseAnnotationValue(annotation.tree)))
else if (isAnnotation(annotation, resolutionTypeName))
update(state, updatedResolution = Some(parseAnnotationValue(annotation.tree)))
else
sys.error(
s"Unexpected annotation detected (${annotations.map(annotationTypeName)} but the only supported ones are $acceptedTypeNames)."
)
}

ErrorDocumentationAnnotations(doc.explanation, doc.resolution)
ErrorDocumentationAnnotations(doc.deprecation, doc.explanation, doc.resolution)
}

private def parseDeprecatedAnnotationValue(tree: ru.Tree): String =
tree
.children(1)
.asInstanceOf[ru.NamedArg]
.children(1)
.asInstanceOf[ru.Literal]
.value
.value
.asInstanceOf[String]

@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))
private def parseAnnotationValue(tree: ru.Tree): String = {
try {
// get second (index starts at 0) child of tree as it contains the first value of the annotation
Seq(1).map(tree.children(_).asInstanceOf[ru.Literal].value.value.asInstanceOf[String]) match {
case s :: Nil => s.stripMargin
case _ => sys.exit(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

package com.daml.error.generator

import com.daml.error.{Explanation, Grouping, Resolution}
import com.daml.error.{Deprecation, Explanation, Grouping, Resolution}

/** Contains error presentation data to be used for documentation rendering on the website.
*
Expand All @@ -22,6 +22,7 @@ case class ErrorDocItem(
hierarchicalGrouping: List[Grouping],
conveyance: String,
code: String,
deprecation: Deprecation,
explanation: Explanation,
resolution: Resolution,
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ object Main {
)

implicit val errorCodeEncode: Encoder[ErrorDocItem] =
Encoder.forProduct7(
Encoder.forProduct8(
"className",
"category",
"hierarchicalGrouping",
"conveyance",
"code",
"deprecation",
"explanation",
"resolution",
)(i =>
Expand All @@ -42,6 +43,7 @@ object Main {
i.hierarchicalGrouping,
i.conveyance,
i.code,
i.deprecation.deprecation,
i.explanation.explanation,
i.resolution.resolution,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package com.daml.error.generator
import com.daml.error.utils.testpackage.subpackage.MildErrors
import com.daml.error.utils.testpackage.subpackage.MildErrors.NotSoSeriousError
import com.daml.error.utils.testpackage.{DeprecatedError, SeriousError}
import com.daml.error.{Explanation, Grouping, Resolution}
import com.daml.error.{Deprecation, Explanation, Grouping, Resolution}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

Expand All @@ -32,6 +32,7 @@ class ErrorCodeDocumentationGeneratorSpec extends AnyFlatSpec with Matchers {
conveyance =
"This error is logged with log-level ERROR on the server side.\nThis error is exposed on the API with grpc-status INTERNAL without any details due to security reasons",
code = "BLUE_SCREEN",
deprecation = Deprecation(""),
explanation = Explanation("Things happen."),
resolution = Resolution("Turn it off and on again."),
),
Expand All @@ -42,6 +43,7 @@ class ErrorCodeDocumentationGeneratorSpec extends AnyFlatSpec with Matchers {
conveyance =
"This error is logged with log-level ERROR on the server side.\nThis error is exposed on the API with grpc-status INTERNAL without any details due to security reasons",
code = "DEPRECATED_ERROR",
deprecation = Deprecation("deprecated."),
explanation = Explanation("Things happen."),
resolution = Resolution("Turn it off and on again."),
),
Expand All @@ -53,6 +55,7 @@ class ErrorCodeDocumentationGeneratorSpec extends AnyFlatSpec with Matchers {
conveyance =
"This error is logged with log-level INFO on the server side.\nThis error is exposed on the API with grpc-status UNAVAILABLE including a detailed error message",
code = "TEST_ROUTINE_FAILURE_PLEASE_IGNORE",
deprecation = Deprecation(""),
explanation = Explanation("Test: Things like this always happen."),
resolution = Resolution("Test: Why not ignore?"),
),
Expand Down
1 change: 1 addition & 0 deletions ledger/error/src/main/scala/com/daml/error/ErrorCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,5 +261,6 @@ object ErrorCode {
}

// Use these annotations to add more information to the documentation for an error on the website
case class Deprecation(deprecation: String) extends StaticAnnotation
case class Explanation(explanation: String) extends StaticAnnotation
case class Resolution(resolution: String) extends StaticAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import com.daml.logging.LoggingContext

@deprecated("deprecated.")
@Resolution("Turn it off and on again.")
@deprecated("deprecated.")
@Explanation("Things happen.")
case object DeprecatedError
extends ErrorCode("DEPRECATED_ERROR", ErrorCategory.SystemInternalAssumptionViolated)(
Expand Down

0 comments on commit 0823601

Please sign in to comment.