Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.gradle.js2p;

import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import org.jsonschema2pojo.Schema;
import org.jsonschema2pojo.exception.GenerationException;
import org.jsonschema2pojo.rules.PropertyRule;
import org.jsonschema2pojo.rules.RuleFactory;

/**
* A {@link PropertyRule} that emits each property's {@code description} only on its getter.
*
* <p>By default jsonschema2pojo writes a property description in three places: the field javadoc,
* the getter javadoc, and a {@code @JsonPropertyDescription} annotation on the field. The getter is
* the public API surface users discover, so we consolidate the description there and drop the field
* javadoc and annotation.
*
* <p>This also fixes a jsonschema2pojo limitation: it follows draft-03/04 semantics where a {@code
* $ref} replaces its sibling keywords, so a {@code description} written as a sibling of {@code $ref}
* is dropped entirely. The configuration schema is draft 2020-12, where {@code $ref} siblings are
* valid; re-applying from the original node restores those descriptions on the getter too.
*
* <p>Mechanism: strip {@code description} from the node before delegating to {@link
* PropertyRule#apply} so the superclass adds it nowhere (not the field javadoc, the getter javadoc,
* or a {@code @JsonPropertyDescription}), then apply it to the getter only — leaving fields without
* any javadoc.
*/
public class OtelPropertyRule extends PropertyRule {

private static final String JSON_PROPERTY_DESCRIPTION = JsonPropertyDescription.class.getName();

private final RuleFactory ruleFactory;

OtelPropertyRule(RuleFactory ruleFactory) {
super(ruleFactory);
this.ruleFactory = ruleFactory;
}

@Override
public JDefinedClass apply(
String nodeName, JsonNode node, JsonNode parent, JDefinedClass jclass, Schema schema) {
JsonNode description = node.get("description");

// Delegate with the description removed so the superclass places it nowhere; we re-apply it to
// the getter below.
JsonNode delegateNode = node;
if (description != null && node.isObject()) {
delegateNode = node.deepCopy();
((ObjectNode) delegateNode).remove("description");
}

JDefinedClass result = super.apply(nodeName, delegateNode, parent, jclass, schema);

String propertyName = ruleFactory.getNameHelper().getPropertyName(nodeName, node);
JFieldVar field = result.fields().get(propertyName);
if (field == null) {
return result;
}

if (hasDescriptionAnnotation(field)) {
throw new GenerationException(
"Property '"
+ nodeName
+ "' resolves to a type that defines its own top-level description. Descriptions on"
+ " $defs are not handled (they would duplicate onto the field). See "
+ OtelPropertyRule.class.getName()
+ ".");
}

if (description != null) {
applyGetterDescription(nodeName, node, schema, result, propertyName, field, description);
}
return result;
}

private void applyGetterDescription(
String nodeName,
JsonNode node,
Schema schema,
JDefinedClass jclass,
String propertyName,
JFieldVar field,
JsonNode description) {
String getterName = ruleFactory.getNameHelper().getGetterName(propertyName, field.type(), node);
for (JMethod method : jclass.methods()) {
if (method.name().equals(getterName)) {
ruleFactory
.getDescriptionRule()
.apply(nodeName, preserveLineBreaks(description), node, method, schema);
return;
}
}
}

// google-java-format reflows javadoc prose, collapsing the schema's single newlines into spaces
// (only blank lines survive, rendered as <p>). Promote each interior lone newline to a blank line
// so every original line becomes its own <p> paragraph. Existing blank lines are left alone, as is
// a trailing newline (so the description doesn't gain a stray trailing blank paragraph).
private static JsonNode preserveLineBreaks(JsonNode description) {
String text = description.asText();
if (text.indexOf('\n') < 0) {
return description;
}
boolean trailingNewline = text.endsWith("\n");
String body = trailingNewline ? text.substring(0, text.length() - 1) : text;
body = body.replaceAll("(?<!\n)\n(?!\n)", "\n\n");
return TextNode.valueOf(trailingNewline ? body + "\n" : body);
}

private static boolean hasDescriptionAnnotation(JFieldVar field) {
for (JAnnotationUse annotation : field.annotations()) {
if (annotation.getAnnotationClass().fullName().equals(JSON_PROPERTY_DESCRIPTION)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package io.opentelemetry.gradle.js2p;

import com.sun.codemodel.JClassContainer;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocCommentable;
import com.sun.codemodel.JPackage;
import com.sun.codemodel.JType;
import org.jsonschema2pojo.rules.ObjectRule;
Expand All @@ -14,10 +16,17 @@
import org.jsonschema2pojo.util.ParcelableHelper;

/**
* Custom {@link RuleFactory} that swaps in {@link OtelObjectRule} so generated POJOs get
* AutoValue-style {@code toString}/{@code equals}/{@code hashCode} implementations instead of
* jsonschema2pojo's defaults, and {@link OtelEnumRule} so top-level experimental enums are routed
* into the {@code internal} sub-package.
* Custom {@link RuleFactory} for the declarative-config POJO generation. It swaps in:
*
* <ul>
* <li>{@link OtelObjectRule} — AutoValue-style {@code toString}/{@code equals}/{@code hashCode}
* instead of jsonschema2pojo's defaults, and routes experimental classes into the {@code
* internal} sub-package.
* <li>{@link OtelEnumRule} — routes top-level experimental enums into the {@code internal}
* sub-package, mirroring {@link OtelObjectRule} for classes.
* <li>{@link OtelPropertyRule} — restores property descriptions written as siblings of {@code
* $ref}, which jsonschema2pojo otherwise drops.
* </ul>
*
* <p>Referenced from {@code sdk-extensions/declarative-config/build.gradle.kts} via {@code
* jsonSchema2Pojo.customRuleFactory}.
Expand All @@ -33,4 +42,23 @@ public Rule<JPackage, JType> getObjectRule() {
public Rule<JClassContainer, JType> getEnumRule() {
return new OtelEnumRule(this);
}

@Override
public Rule<JDefinedClass, JDefinedClass> getPropertyRule() {
return new OtelPropertyRule(this);
}

// The opentelemetry-configuration schema already documents required-ness in each required
// property's description ("Property is required and must be non-null."), so jsonschema2pojo's
// "(Required)" javadoc is purely duplicative. No-op the required rules to drop it. With JSR-303
// and JSR-305 annotations disabled, the "(Required)" javadoc is these rules' only effect.
@Override
public Rule<JDefinedClass, JDefinedClass> getRequiredArrayRule() {
return (nodeName, node, parent, generatableType, schema) -> generatableType;
}

@Override
public Rule<JDocCommentable, JDocCommentable> getRequiredRule() {
return (nodeName, node, parent, generatableType, schema) -> generatableType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ public class AggregationModel {
@Nullable
private SumAggregationModel sum;

/**
* Configures the stream to use the instrument kind to select an aggregation and advisory
* parameters to influence aggregation configuration parameters. See
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#default-aggregation
* for details.
*
* <p>If omitted, ignore.
*/
@JsonProperty("default")
@Nullable
public DefaultAggregationModel getDefault() {
Expand All @@ -58,6 +66,13 @@ public AggregationModel withDefault(DefaultAggregationModel _default) {
return this;
}

/**
* Configures the stream to ignore/drop all instrument measurements. See
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#drop-aggregation
* for details.
*
* <p>If omitted, ignore.
*/
@JsonProperty("drop")
@Nullable
public DropAggregationModel getDrop() {
Expand All @@ -69,6 +84,14 @@ public AggregationModel withDrop(DropAggregationModel drop) {
return this;
}

/**
* Configures the stream to collect data for the histogram metric point using a set of explicit
* boundary values for histogram bucketing. See
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation
* for details
*
* <p>If omitted, ignore.
*/
@JsonProperty("explicit_bucket_histogram")
@Nullable
public ExplicitBucketHistogramAggregationModel getExplicitBucketHistogram() {
Expand All @@ -81,6 +104,15 @@ public AggregationModel withExplicitBucketHistogram(
return this;
}

/**
* Configures the stream to collect data for the exponential histogram metric point, which uses a
* base-2 exponential formula to determine bucket boundaries and an integer scale parameter to
* control resolution. See
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation
* for details.
*
* <p>If omitted, ignore.
*/
@JsonProperty("base2_exponential_bucket_histogram")
@Nullable
public Base2ExponentialBucketHistogramAggregationModel getBase2ExponentialBucketHistogram() {
Expand All @@ -93,6 +125,13 @@ public AggregationModel withBase2ExponentialBucketHistogram(
return this;
}

/**
* Configures the stream to collect data using the last measurement. See
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#last-value-aggregation
* for details.
*
* <p>If omitted, ignore.
*/
@JsonProperty("last_value")
@Nullable
public LastValueAggregationModel getLastValue() {
Expand All @@ -104,6 +143,13 @@ public AggregationModel withLastValue(LastValueAggregationModel lastValue) {
return this;
}

/**
* Configures the stream to collect the arithmetic sum of measurement values. See
* https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#sum-aggregation
* for details.
*
* <p>If omitted, ignore.
*/
@JsonProperty("sum")
@Nullable
public SumAggregationModel getSum() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import javax.annotation.Generated;
import javax.annotation.Nullable;
Expand All @@ -17,26 +16,20 @@
@Generated("jsonschema2pojo")
public class AttributeLimitsModel {

/**
* Configure max attribute value size. Value must be non-negative. If omitted or null, there is no
* limit.
*/
@JsonProperty("attribute_value_length_limit")
@JsonPropertyDescription(
"Configure max attribute value size. \nValue must be non-negative.\nIf omitted or null, there is no limit.\n")
@Nullable
private Integer attributeValueLengthLimit;

/** Configure max attribute count. Value must be non-negative. If omitted or null, 128 is used. */
@JsonProperty("attribute_count_limit")
@JsonPropertyDescription(
"Configure max attribute count. \nValue must be non-negative.\nIf omitted or null, 128 is used.\n")
@Nullable
private Integer attributeCountLimit;

/**
* Configure max attribute value size. Value must be non-negative. If omitted or null, there is no
* limit.
* Configure max attribute value size.
*
* <p>Value must be non-negative.
*
* <p>If omitted or null, there is no limit.
*/
@JsonProperty("attribute_value_length_limit")
@Nullable
Expand All @@ -49,7 +42,13 @@ public AttributeLimitsModel withAttributeValueLengthLimit(Integer attributeValue
return this;
}

/** Configure max attribute count. Value must be non-negative. If omitted or null, 128 is used. */
/**
* Configure max attribute count.
*
* <p>Value must be non-negative.
*
* <p>If omitted or null, 128 is used.
*/
@JsonProperty("attribute_count_limit")
@Nullable
public Integer getAttributeCountLimit() {
Expand Down
Loading
Loading