This is an automated email from the ASF dual-hosted git repository. yasith pushed a commit to branch feat/thrift-server-extraction in repository https://gitbox.apache.org/repos/asf/airavata.git
commit 588de0a192999d40ecc4ae6cc67b4d1af2ff9c15 Author: yasithdev <[email protected]> AuthorDate: Thu Mar 26 14:32:19 2026 -0500 feat: add representative MapStruct mapper for workspace types WorkspaceMapper maps bidirectionally between: - Thrift: org.apache.airavata.model.workspace.Project - Proto: org.apache.airavata.model.workspace.proto.Project Key design decisions: - @ObjectFactory provides Project.newBuilder() since protobuf builders have no public no-arg constructor for MapStruct to invoke - Repeated fields (sharedUsers, sharedGroups) handled via @AfterMapping using builder.addAllXxx() to avoid MapStruct attempting to instantiate the abstract ProtocolStringList type - protoToThrift wraps ProtocolStringList into ArrayList naturally --- .../api/server/mapper/WorkspaceMapper.java | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/airavata-thrift-server/src/main/java/org/apache/airavata/api/server/mapper/WorkspaceMapper.java b/airavata-thrift-server/src/main/java/org/apache/airavata/api/server/mapper/WorkspaceMapper.java new file mode 100644 index 0000000000..44dad11244 --- /dev/null +++ b/airavata-thrift-server/src/main/java/org/apache/airavata/api/server/mapper/WorkspaceMapper.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.airavata.api.server.mapper; + +import java.util.List; +import org.apache.airavata.model.workspace.proto.Project; +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; +import org.mapstruct.factory.Mappers; + +/** + * MapStruct mapper between Thrift-generated and Protobuf-generated workspace types. + * + * <p>Thrift classes live in {@code org.apache.airavata.model.workspace}; Proto classes live in + * {@code org.apache.airavata.model.workspace.proto}. The packages are distinct — proto files use + * {@code java_package} with a {@code .proto} suffix to avoid duplicate class errors with the + * identically-named Thrift-generated classes. + * + * <p>Field name differences: + * <ul> + * <li>Thrift {@code projectID} ↔ Proto {@code projectId} + * <li>Thrift {@code sharedUsers} / {@code sharedGroups} → handled via {@code @AfterMapping} + * because Protobuf builder repeated fields use {@code addAllXxx()} rather than setters + * </ul> + * + * <p>Protobuf messages are immutable; MapStruct populates the {@link Project.Builder} provided by + * {@link #createProjectBuilder()}, then the generated implementation calls {@code .build()}. + */ +@Mapper +public interface WorkspaceMapper { + + WorkspaceMapper INSTANCE = Mappers.getMapper(WorkspaceMapper.class); + + /** + * Supplies the Protobuf builder instance. MapStruct uses this via {@code @ObjectFactory} + * instead of trying to invoke a (non-existent) public no-arg constructor on + * {@link Project.Builder}. + */ + @ObjectFactory + default Project.Builder createProjectBuilder() { + return Project.newBuilder(); + } + + /** + * Maps a Thrift {@link org.apache.airavata.model.workspace.Project} to a Protobuf + * {@link Project.Builder}. Repeated fields are populated by + * {@link #fillRepeatedFields(org.apache.airavata.model.workspace.Project, Project.Builder)}. + * Call {@code .build()} on the result to obtain the immutable {@link Project}. + */ + @Mapping(source = "projectID", target = "projectId") + @Mapping(target = "sharedUsersList", ignore = true) + @Mapping(target = "sharedGroupsList", ignore = true) + Project.Builder thriftToProtoBuilder(org.apache.airavata.model.workspace.Project thrift); + + /** + * Populates repeated Protobuf fields after the main scalar mapping, using the builder's + * {@code addAllXxx()} API which avoids attempts to instantiate the abstract + * {@code ProtocolStringList}. + */ + @AfterMapping + default void fillRepeatedFields( + org.apache.airavata.model.workspace.Project thrift, + @MappingTarget Project.Builder builder) { + List<String> users = thrift.getSharedUsers(); + if (users != null) { + builder.addAllSharedUsers(users); + } + List<String> groups = thrift.getSharedGroups(); + if (groups != null) { + builder.addAllSharedGroups(groups); + } + } + + /** + * Maps a Protobuf {@link Project} to its Thrift counterpart. + * + * <p>Protobuf repeated fields are accessed via {@code getSharedUsersList()} / {@code + * getSharedGroupsList()}, which MapStruct resolves from the property names. + */ + @Mapping(source = "projectId", target = "projectID") + @Mapping(source = "sharedUsersList", target = "sharedUsers") + @Mapping(source = "sharedGroupsList", target = "sharedGroups") + org.apache.airavata.model.workspace.Project protoToThrift(Project proto); +}
