Skip to content

Commit a78d3f2

Browse files
committed
Next batch
1 parent 5ce4383 commit a78d3f2

File tree

3 files changed

+126
-66
lines changed

3 files changed

+126
-66
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.a2a.grpc.mapper;
2+
3+
import org.mapstruct.CollectionMappingStrategy;
4+
import org.mapstruct.Mapper;
5+
import org.mapstruct.Mapping;
6+
import org.mapstruct.factory.Mappers;
7+
8+
/**
9+
* Mapper between {@link io.a2a.spec.PushNotificationConfig} and {@link io.a2a.grpc.PushNotificationConfig}.
10+
*/
11+
@Mapper(config = ProtoMapperConfig.class,
12+
collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
13+
uses = AuthenticationInfoMapper.class)
14+
public interface PushNotificationConfigMapper {
15+
16+
PushNotificationConfigMapper INSTANCE = Mappers.getMapper(PushNotificationConfigMapper.class);
17+
18+
@IgnoreProtobufInternals
19+
@Mapping(target = "url", source = "url", conditionExpression = "java(domain.url() != null)")
20+
@Mapping(target = "token", source = "token", conditionExpression = "java(domain.token() != null)")
21+
@Mapping(target = "authentication", source = "authentication", conditionExpression = "java(domain.authentication() != null)")
22+
@Mapping(target = "id", source = "id", conditionExpression = "java(domain.id() != null)")
23+
io.a2a.grpc.PushNotificationConfig toProto(io.a2a.spec.PushNotificationConfig domain);
24+
25+
/**
26+
* Converts proto PushNotificationConfig to domain.
27+
* Handles protobuf empty string → null conversion for optional fields.
28+
*/
29+
default io.a2a.spec.PushNotificationConfig fromProto(io.a2a.grpc.PushNotificationConfig proto) {
30+
if (proto == null) {
31+
return null;
32+
}
33+
34+
String token = proto.getToken().isEmpty() ? null : proto.getToken();
35+
String id = proto.getId().isEmpty() ? null : proto.getId();
36+
io.a2a.spec.AuthenticationInfo auth = proto.hasAuthentication() ?
37+
AuthenticationInfoMapper.INSTANCE.fromProto(proto.getAuthentication()) : null;
38+
39+
return new io.a2a.spec.PushNotificationConfig(
40+
proto.getUrl(),
41+
token,
42+
auth,
43+
id
44+
);
45+
}
46+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.a2a.grpc.mapper;
2+
3+
import org.mapstruct.Mapper;
4+
import org.mapstruct.factory.Mappers;
5+
6+
/**
7+
* Mapper between {@link io.a2a.spec.TaskState} and {@link io.a2a.grpc.TaskState}.
8+
* <p>
9+
* Handles the conversion between domain TaskState enum (with string-based wire format)
10+
* and protobuf TaskState enum (with integer-based wire format and TASK_STATE_ prefix).
11+
* <p>
12+
* Note: Proto uses CANCELLED spelling while domain uses CANCELED.
13+
*/
14+
@Mapper(config = ProtoMapperConfig.class)
15+
public interface TaskStateMapper {
16+
17+
TaskStateMapper INSTANCE = Mappers.getMapper(TaskStateMapper.class);
18+
19+
/**
20+
* Converts domain TaskState to proto TaskState.
21+
*
22+
* @param domain the domain task state
23+
* @return the proto task state, or TASK_STATE_UNSPECIFIED if input is null
24+
*/
25+
default io.a2a.grpc.TaskState toProto(io.a2a.spec.TaskState domain) {
26+
if (domain == null) {
27+
return io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED;
28+
}
29+
30+
return switch (domain) {
31+
case SUBMITTED -> io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED;
32+
case WORKING -> io.a2a.grpc.TaskState.TASK_STATE_WORKING;
33+
case INPUT_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_INPUT_REQUIRED;
34+
case AUTH_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_AUTH_REQUIRED;
35+
case COMPLETED -> io.a2a.grpc.TaskState.TASK_STATE_COMPLETED;
36+
case CANCELED -> io.a2a.grpc.TaskState.TASK_STATE_CANCELLED; // Note spelling difference
37+
case FAILED -> io.a2a.grpc.TaskState.TASK_STATE_FAILED;
38+
case REJECTED -> io.a2a.grpc.TaskState.TASK_STATE_REJECTED;
39+
case UNKNOWN -> io.a2a.grpc.TaskState.UNRECOGNIZED;
40+
};
41+
}
42+
43+
/**
44+
* Converts proto TaskState to domain TaskState.
45+
*
46+
* @param proto the proto task state
47+
* @return the domain task state, or UNKNOWN if input is null or unrecognized
48+
*/
49+
default io.a2a.spec.TaskState fromProto(io.a2a.grpc.TaskState proto) {
50+
if (proto == null) {
51+
return io.a2a.spec.TaskState.UNKNOWN;
52+
}
53+
54+
return switch (proto) {
55+
case TASK_STATE_SUBMITTED -> io.a2a.spec.TaskState.SUBMITTED;
56+
case TASK_STATE_WORKING -> io.a2a.spec.TaskState.WORKING;
57+
case TASK_STATE_INPUT_REQUIRED -> io.a2a.spec.TaskState.INPUT_REQUIRED;
58+
case TASK_STATE_AUTH_REQUIRED -> io.a2a.spec.TaskState.AUTH_REQUIRED;
59+
case TASK_STATE_COMPLETED -> io.a2a.spec.TaskState.COMPLETED;
60+
case TASK_STATE_CANCELLED -> io.a2a.spec.TaskState.CANCELED; // Note spelling difference
61+
case TASK_STATE_FAILED -> io.a2a.spec.TaskState.FAILED;
62+
case TASK_STATE_REJECTED -> io.a2a.spec.TaskState.REJECTED;
63+
case TASK_STATE_UNSPECIFIED, UNRECOGNIZED -> io.a2a.spec.TaskState.UNKNOWN;
64+
};
65+
}
66+
}

spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java

Lines changed: 14 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import io.a2a.grpc.mapper.OAuthFlowsMapper;
3333
import io.a2a.grpc.mapper.OpenIdConnectSecuritySchemeMapper;
3434
import io.a2a.grpc.mapper.PasswordOAuthFlowMapper;
35+
import io.a2a.grpc.mapper.PushNotificationConfigMapper;
3536
import io.a2a.grpc.mapper.SecuritySchemeMapper;
37+
import io.a2a.grpc.mapper.TaskStateMapper;
3638
import io.a2a.spec.APIKeySecurityScheme;
3739
import io.a2a.spec.AgentCapabilities;
3840
import io.a2a.spec.AgentCard;
@@ -148,20 +150,7 @@ public static io.a2a.grpc.TaskPushNotificationConfig taskPushNotificationConfig(
148150
}
149151

150152
private static io.a2a.grpc.PushNotificationConfig pushNotificationConfig(PushNotificationConfig config) {
151-
io.a2a.grpc.PushNotificationConfig.Builder builder = io.a2a.grpc.PushNotificationConfig.newBuilder();
152-
if (config.url() != null) {
153-
builder.setUrl(config.url());
154-
}
155-
if (config.token() != null) {
156-
builder.setToken(config.token());
157-
}
158-
if (config.authentication() != null) {
159-
builder.setAuthentication(authenticationInfo(config.authentication()));
160-
}
161-
if (config.id() != null) {
162-
builder.setId(config.id());
163-
}
164-
return builder.build();
153+
return PushNotificationConfigMapper.INSTANCE.toProto(config);
165154
}
166155

167156
public static io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent(TaskArtifactUpdateEvent event) {
@@ -275,29 +264,7 @@ private static io.a2a.grpc.TaskStatus taskStatus(TaskStatus taskStatus) {
275264
}
276265

277266
public static io.a2a.grpc.TaskState taskState(TaskState taskState) {
278-
if (taskState == null) {
279-
return io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED;
280-
}
281-
return switch (taskState) {
282-
case SUBMITTED ->
283-
io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED;
284-
case WORKING ->
285-
io.a2a.grpc.TaskState.TASK_STATE_WORKING;
286-
case INPUT_REQUIRED ->
287-
io.a2a.grpc.TaskState.TASK_STATE_INPUT_REQUIRED;
288-
case AUTH_REQUIRED ->
289-
io.a2a.grpc.TaskState.TASK_STATE_AUTH_REQUIRED;
290-
case COMPLETED ->
291-
io.a2a.grpc.TaskState.TASK_STATE_COMPLETED;
292-
case CANCELED ->
293-
io.a2a.grpc.TaskState.TASK_STATE_CANCELLED;
294-
case FAILED ->
295-
io.a2a.grpc.TaskState.TASK_STATE_FAILED;
296-
case REJECTED ->
297-
io.a2a.grpc.TaskState.TASK_STATE_REJECTED;
298-
default ->
299-
io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED;
300-
};
267+
return TaskStateMapper.INSTANCE.toProto(taskState);
301268
}
302269

303270
private static io.a2a.grpc.AuthenticationInfo authenticationInfo(AuthenticationInfo authenticationInfo) {
@@ -658,12 +625,13 @@ private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.Sen
658625
if(pushNotification == null || pushNotification.getDefaultInstanceForType().equals(pushNotification)) {
659626
return null;
660627
}
661-
return new PushNotificationConfig(
662-
pushNotification.getUrl(),
663-
pushNotification.getToken().isEmpty() ? null : pushNotification.getToken(),
664-
pushNotification.hasAuthentication() ? authenticationInfo(pushNotification.getAuthentication()) : null,
665-
pushNotification.getId().isEmpty() ? configId : pushNotification.getId()
666-
);
628+
io.a2a.grpc.PushNotificationConfig proto = (io.a2a.grpc.PushNotificationConfig) pushNotification;
629+
PushNotificationConfig result = PushNotificationConfigMapper.INSTANCE.fromProto(proto);
630+
// Override ID if different configId provided
631+
if (configId != null && !configId.isEmpty() && !configId.equals(result.id())) {
632+
return new PushNotificationConfig(result.url(), result.token(), result.authentication(), configId);
633+
}
634+
return result;
667635
}
668636

669637
private static @Nullable PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification) {
@@ -793,31 +761,11 @@ private static DataPart dataPart(io.a2a.grpc.DataPartOrBuilder dataPart) {
793761
}
794762

795763
private static @Nullable TaskState taskState(io.a2a.grpc.TaskState taskState) {
796-
if (taskState == null) {
764+
if (taskState == null || taskState == io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED) {
797765
return null;
798766
}
799-
return switch (taskState) {
800-
case TASK_STATE_SUBMITTED ->
801-
TaskState.SUBMITTED;
802-
case TASK_STATE_WORKING ->
803-
TaskState.WORKING;
804-
case TASK_STATE_INPUT_REQUIRED ->
805-
TaskState.INPUT_REQUIRED;
806-
case TASK_STATE_AUTH_REQUIRED ->
807-
TaskState.AUTH_REQUIRED;
808-
case TASK_STATE_COMPLETED ->
809-
TaskState.COMPLETED;
810-
case TASK_STATE_CANCELLED ->
811-
TaskState.CANCELED;
812-
case TASK_STATE_FAILED ->
813-
TaskState.FAILED;
814-
case TASK_STATE_REJECTED ->
815-
TaskState.REJECTED;
816-
case TASK_STATE_UNSPECIFIED ->
817-
null;
818-
case UNRECOGNIZED ->
819-
null;
820-
};
767+
TaskState result = TaskStateMapper.INSTANCE.fromProto(taskState);
768+
return result == TaskState.UNKNOWN ? null : result;
821769
}
822770

823771
private static @Nullable Map<String, Object> struct(Struct struct) {

0 commit comments

Comments
 (0)