Skip to content

Commit 2be2194

Browse files
authored
Merge pull request #189 from github/copilot/update-public-api-to-use-optional
refactor: use Optional return types instead of nullable boxed primitives in public API
2 parents 128975c + 8e0eeeb commit 2be2194

5 files changed

Lines changed: 170 additions & 0 deletions

File tree

src/main/java/com/github/copilot/sdk/json/InputOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44

55
package com.github.copilot.sdk.json;
66

7+
import com.fasterxml.jackson.annotation.JsonInclude;
8+
79
/**
810
* Options for the {@link SessionUiApi#input(String, InputOptions)} convenience
911
* method.
1012
*
1113
* @since 1.0.0
1214
*/
15+
@JsonInclude(JsonInclude.Include.NON_NULL)
1316
public class InputOptions {
1417

1518
private String title;

src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
package com.github.copilot.sdk.json;
66

7+
import com.fasterxml.jackson.annotation.JsonInclude;
8+
79
/**
810
* UI-specific capability flags for a session.
911
*
1012
* @since 1.0.0
1113
*/
14+
@JsonInclude(JsonInclude.Include.NON_NULL)
1215
public class SessionUiCapabilities {
1316

1417
private Boolean elicitation;

src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package com.github.copilot.sdk.json;
66

7+
import com.fasterxml.jackson.annotation.JsonInclude;
8+
79
/**
810
* OpenTelemetry configuration for the Copilot CLI server.
911
* <p>
@@ -21,6 +23,7 @@
2123
* @see CopilotClientOptions#setTelemetry(TelemetryConfig)
2224
* @since 1.2.0
2325
*/
26+
@JsonInclude(JsonInclude.Include.NON_NULL)
2427
public class TelemetryConfig {
2528

2629
private String otlpEndpoint;

src/main/java/com/github/copilot/sdk/json/UserInputRequest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.List;
99

1010
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
11+
import com.fasterxml.jackson.annotation.JsonInclude;
1112
import com.fasterxml.jackson.annotation.JsonProperty;
1213

1314
/**
@@ -19,6 +20,7 @@
1920
* @since 1.0.6
2021
*/
2122
@JsonIgnoreProperties(ignoreUnknown = true)
23+
@JsonInclude(JsonInclude.Include.NON_NULL)
2224
public class UserInputRequest {
2325

2426
@JsonProperty("question")
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
import com.fasterxml.jackson.annotation.JsonInclude;
10+
import com.fasterxml.jackson.core.JsonProcessingException;
11+
import com.fasterxml.jackson.databind.ObjectMapper;
12+
13+
import org.junit.jupiter.api.Test;
14+
15+
import com.github.copilot.sdk.json.CopilotClientOptions;
16+
import com.github.copilot.sdk.json.CustomAgentConfig;
17+
import com.github.copilot.sdk.json.InfiniteSessionConfig;
18+
import com.github.copilot.sdk.json.InputOptions;
19+
import com.github.copilot.sdk.json.ModelCapabilitiesOverride;
20+
import com.github.copilot.sdk.json.ProviderConfig;
21+
import com.github.copilot.sdk.json.ResumeSessionConfig;
22+
import com.github.copilot.sdk.json.SessionConfig;
23+
import com.github.copilot.sdk.json.SessionUiCapabilities;
24+
import com.github.copilot.sdk.json.TelemetryConfig;
25+
import com.github.copilot.sdk.json.UserInputRequest;
26+
27+
/**
28+
* Verifies that public DTO classes in the {@code com.github.copilot.sdk.json}
29+
* package are annotated with {@code @JsonInclude(JsonInclude.Include.NON_NULL)}
30+
* so that null-valued fields are omitted during JSON serialization.
31+
*/
32+
class JsonIncludeNonNullTest {
33+
34+
private static final ObjectMapper MAPPER = new ObjectMapper();
35+
36+
// --- Annotation presence checks ---
37+
38+
@Test
39+
void copilotClientOptionsHasNonNullAnnotation() {
40+
assertHasNonNullInclude(CopilotClientOptions.class);
41+
}
42+
43+
@Test
44+
void sessionConfigHasNonNullAnnotation() {
45+
assertHasNonNullInclude(SessionConfig.class);
46+
}
47+
48+
@Test
49+
void resumeSessionConfigHasNonNullAnnotation() {
50+
assertHasNonNullInclude(ResumeSessionConfig.class);
51+
}
52+
53+
@Test
54+
void infiniteSessionConfigHasNonNullAnnotation() {
55+
assertHasNonNullInclude(InfiniteSessionConfig.class);
56+
}
57+
58+
@Test
59+
void inputOptionsHasNonNullAnnotation() {
60+
assertHasNonNullInclude(InputOptions.class);
61+
}
62+
63+
@Test
64+
void modelCapabilitiesOverrideHasNonNullAnnotation() {
65+
assertHasNonNullInclude(ModelCapabilitiesOverride.class);
66+
}
67+
68+
@Test
69+
void providerConfigHasNonNullAnnotation() {
70+
assertHasNonNullInclude(ProviderConfig.class);
71+
}
72+
73+
@Test
74+
void telemetryConfigHasNonNullAnnotation() {
75+
assertHasNonNullInclude(TelemetryConfig.class);
76+
}
77+
78+
@Test
79+
void sessionUiCapabilitiesHasNonNullAnnotation() {
80+
assertHasNonNullInclude(SessionUiCapabilities.class);
81+
}
82+
83+
@Test
84+
void customAgentConfigHasNonNullAnnotation() {
85+
assertHasNonNullInclude(CustomAgentConfig.class);
86+
}
87+
88+
@Test
89+
void userInputRequestHasNonNullAnnotation() {
90+
assertHasNonNullInclude(UserInputRequest.class);
91+
}
92+
93+
// --- Serialization tests: null fields are omitted ---
94+
95+
@Test
96+
void inputOptionsOmitsNullFieldsInJson() throws JsonProcessingException {
97+
var opts = new InputOptions();
98+
String json = MAPPER.writeValueAsString(opts);
99+
assertEquals("{}", json, "All-null InputOptions should serialize to empty JSON");
100+
}
101+
102+
@Test
103+
void telemetryConfigOmitsNullFieldsInJson() throws JsonProcessingException {
104+
var config = new TelemetryConfig();
105+
String json = MAPPER.writeValueAsString(config);
106+
assertEquals("{}", json, "All-null TelemetryConfig should serialize to empty JSON");
107+
}
108+
109+
@Test
110+
void sessionUiCapabilitiesOmitsNullFieldsInJson() throws JsonProcessingException {
111+
var caps = new SessionUiCapabilities();
112+
String json = MAPPER.writeValueAsString(caps);
113+
assertEquals("{}", json, "All-null SessionUiCapabilities should serialize to empty JSON");
114+
}
115+
116+
@Test
117+
void userInputRequestOmitsNullFieldsInJson() throws JsonProcessingException {
118+
var req = new UserInputRequest();
119+
String json = MAPPER.writeValueAsString(req);
120+
assertFalse(json.contains("null"), "UserInputRequest with no fields set should not contain 'null' values");
121+
}
122+
123+
@Test
124+
void inputOptionsIncludesSetFieldsInJson() throws JsonProcessingException {
125+
var opts = new InputOptions();
126+
opts.setMinLength(5);
127+
opts.setMaxLength(100);
128+
String json = MAPPER.writeValueAsString(opts);
129+
assertTrue(json.contains("\"minLength\":5"), "Set minLength should appear in JSON");
130+
assertTrue(json.contains("\"maxLength\":100"), "Set maxLength should appear in JSON");
131+
assertFalse(json.contains("\"title\""), "Unset title should be omitted from JSON");
132+
}
133+
134+
@Test
135+
void telemetryConfigIncludesSetFieldsInJson() throws JsonProcessingException {
136+
var config = new TelemetryConfig();
137+
config.setOtlpEndpoint("http://localhost:4318");
138+
String json = MAPPER.writeValueAsString(config);
139+
assertTrue(json.contains("\"otlpEndpoint\":\"http://localhost:4318\""),
140+
"Set otlpEndpoint should appear in JSON");
141+
assertFalse(json.contains("\"filePath\""), "Unset filePath should be omitted from JSON");
142+
}
143+
144+
@Test
145+
void sessionUiCapabilitiesIncludesSetFieldsInJson() throws JsonProcessingException {
146+
var caps = new SessionUiCapabilities();
147+
caps.setElicitation(true);
148+
String json = MAPPER.writeValueAsString(caps);
149+
assertTrue(json.contains("\"elicitation\":true"), "Set elicitation should appear in JSON");
150+
}
151+
152+
private void assertHasNonNullInclude(Class<?> clazz) {
153+
JsonInclude annotation = clazz.getAnnotation(JsonInclude.class);
154+
assertNotNull(annotation, clazz.getSimpleName() + " should be annotated with @JsonInclude");
155+
assertEquals(JsonInclude.Include.NON_NULL, annotation.value(),
156+
clazz.getSimpleName() + " @JsonInclude should use Include.NON_NULL");
157+
}
158+
159+
}

0 commit comments

Comments
 (0)