From 742f5defd98e316d04d1ffed1a05567ee0c2466a Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:36:00 -0500 Subject: [PATCH 01/14] [BI-2539] - added GitHub OAuth support --- .env.template | 4 ++ pom.xml | 5 ++ .../api/auth/GithubApiClient.java | 32 ++++++++++ .../breedinginsight/api/auth/GithubUser.java | 37 +++++++++++ .../api/auth/GithubUserDetailsMapper.java | 61 +++++++++++++++++++ src/main/resources/application.yml | 11 ++++ 6 files changed, 150 insertions(+) create mode 100644 src/main/java/org/breedinginsight/api/auth/GithubApiClient.java create mode 100644 src/main/java/org/breedinginsight/api/auth/GithubUser.java create mode 100644 src/main/java/org/breedinginsight/api/auth/GithubUserDetailsMapper.java diff --git a/.env.template b/.env.template index 66a62fb5d..5d9e7b531 100644 --- a/.env.template +++ b/.env.template @@ -3,6 +3,10 @@ USER_ID= GROUP_ID= +# GitHub OAuth variables. Only required if using GitHub as an alternative to ORCID. +GITHUB_OAUTH_CLIENT_ID= +GITHUB_OAUTH_CLIENT_SECRET= + ORCID_SANDBOX_AUTHENTICATION=use the Sandbox Orcid, false=>use the Production Orcid. Defaults to false.> # Authentication variables diff --git a/pom.xml b/pom.xml index 4280fd0b1..28d93a5a8 100644 --- a/pom.xml +++ b/pom.xml @@ -190,6 +190,11 @@ micronaut-inject compile + + io.micronaut + micronaut-http-client + compile + io.micronaut micronaut-validation diff --git a/src/main/java/org/breedinginsight/api/auth/GithubApiClient.java b/src/main/java/org/breedinginsight/api/auth/GithubApiClient.java new file mode 100644 index 000000000..a25bb9f41 --- /dev/null +++ b/src/main/java/org/breedinginsight/api/auth/GithubApiClient.java @@ -0,0 +1,32 @@ +/* + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * Licensed 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.breedinginsight.api.auth; + +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Header; +import io.micronaut.http.client.annotation.Client; +import io.reactivex.Flowable; + +@Header(name = "User-Agent", value = "Micronaut") +@Client("https://api.github.com") +public interface GithubApiClient { + + @Get("/user") + Flowable getUser(@Header("Authorization") String authorization); +} + diff --git a/src/main/java/org/breedinginsight/api/auth/GithubUser.java b/src/main/java/org/breedinginsight/api/auth/GithubUser.java new file mode 100644 index 000000000..a71dc64fc --- /dev/null +++ b/src/main/java/org/breedinginsight/api/auth/GithubUser.java @@ -0,0 +1,37 @@ +/* + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * Licensed 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.breedinginsight.api.auth; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.micronaut.core.annotation.Introspected; +import lombok.Getter; + +@Introspected +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +@Getter +public class GithubUser { + + private String id; + // The login will be the unique GitHub username. + private String login; + private String name; + private String email; + +} + diff --git a/src/main/java/org/breedinginsight/api/auth/GithubUserDetailsMapper.java b/src/main/java/org/breedinginsight/api/auth/GithubUserDetailsMapper.java new file mode 100644 index 000000000..02515f766 --- /dev/null +++ b/src/main/java/org/breedinginsight/api/auth/GithubUserDetailsMapper.java @@ -0,0 +1,61 @@ +/* + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * Licensed 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.breedinginsight.api.auth; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.async.publisher.Publishers; +import io.micronaut.security.authentication.AuthenticationResponse; +import io.micronaut.security.authentication.UserDetails; +import io.micronaut.security.oauth2.endpoint.authorization.state.State; +import io.micronaut.security.oauth2.endpoint.token.response.OauthUserDetailsMapper; +import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Publisher; + + +import javax.inject.Named; +import javax.inject.Singleton; +import java.util.Collections; +import java.util.List; + +@Slf4j +@Named("github") +@Singleton +class GithubUserDetailsMapper implements OauthUserDetailsMapper { + + private final GithubApiClient apiClient; + + GithubUserDetailsMapper(GithubApiClient apiClient) { + this.apiClient = apiClient; + } + + @Override + public Publisher createUserDetails(TokenResponse tokenResponse) { + return Publishers.just(new UnsupportedOperationException()); + } + + @Override + public Publisher createAuthenticationResponse(TokenResponse tokenResponse, @Nullable State state) { + return apiClient.getUser("token " + tokenResponse.getAccessToken()) + .map(user -> { + List roles = Collections.singletonList("ROLE_GITHUB"); + return new UserDetails(user.getLogin(), roles); + }); + } +} + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 85987ef9d..3550a56b9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -59,6 +59,17 @@ micronaut: jwks-uri: ${OAUTH_OPENID_JWKSURI:`https://sandbox.orcid.org/oauth/jwks`} user-info: url: ${OAUTH_OPENID_USERINFOURL:`https://sandbox.orcid.org/oauth/userinfo`} + github: + client-id: ${GITHUB_OAUTH_CLIENT_ID} + client-secret: ${GITHUB_OAUTH_CLIENT_SECRET} + scopes: + - user:email + - read:user + authorization: + url: https://github.com/login/oauth/authorize + token: + url: https://github.com/login/oauth/access_token + auth-method: client-secret-basic state: cookie: cookie-max-age: 10m From a3b3f618dd9d87e2ae4529ee96f048dea2e1db6d Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:34:15 -0400 Subject: [PATCH 02/14] [BI-2540] - renamed orcid, added oauth_provider column --- .../api/auth/AuthServiceLoginHandler.java | 11 +++++----- .../breedinginsight/daos/ProgramUserDAO.java | 4 ++-- .../org/breedinginsight/daos/UserDAO.java | 4 ++-- .../daos/impl/UserDAOImpl.java | 6 ++--- .../java/org/breedinginsight/model/User.java | 13 ++++++----- .../breedinginsight/services/UserService.java | 22 +++++++++---------- .../response/mappers/UserQueryMapper.java | 2 +- .../db/migration/V1.33.0__rename-orcid.sql | 22 +++++++++++++++++++ ...mSecuredAnnotationRuleIntegrationTest.java | 6 ++--- ...eedingMethodControllerIntegrationTest.java | 2 +- .../ExperimentControllerIntegrationTest.java | 4 ++-- .../ImportControllerIntegrationTest.java | 2 +- .../InternalServerErrorHandlerUnitTest.java | 2 +- .../JobControllerIntegrationTest.java | 3 +-- .../OntologyControllerIntegrationTest.java | 2 +- .../ProgramControllerIntegrationTest.java | 11 ++++------ ...leSubmissionControllerIntegrationTest.java | 3 +-- .../api/v1/controller/TestTokenValidator.java | 8 +++---- .../TraitControllerIntegrationTest.java | 2 +- .../UploadControllerIntegrationTest.java | 2 +- .../UserControllerIntegrationTest.java | 8 +++---- .../BrAPIServiceFilterIntegrationTest.java | 2 +- .../MetadataFilterIntegrationTest.java | 4 ++-- ...apiAuthorizeControllerIntegrationTest.java | 2 +- ...ionVariablesControllerIntegrationTest.java | 4 ++-- ...vationLevelsControllerIntegrationTest.java | 2 +- ...ervationUnitControllerIntegrationTest.java | 2 +- ...ObservationsControllerIntegrationTest.java | 2 +- .../brapi/v2/BrAPITestUtils.java | 6 ++--- .../v2/BrAPIV2ControllerIntegrationTest.java | 2 +- ...tionVariableControllerIntegrationTest.java | 2 +- .../GermplasmControllerIntegrationTest.java | 2 +- .../brapps/importer/ImportTestUtils.java | 2 +- .../daos/BrAPIObservationUnitDAOTest.java | 2 +- .../DSLTransactionResultIntegrationTest.java | 3 +-- .../TraitValidatorIntegrationTest.java | 2 +- .../ResponseUtilsIntegrationTest.java | 2 +- .../mappers/UserQueryMapperUnitTest.java | 4 ++-- 38 files changed, 100 insertions(+), 84 deletions(-) create mode 100644 src/main/resources/db/migration/V1.33.0__rename-orcid.sql diff --git a/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java b/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java index 3a25eae13..8e210f608 100644 --- a/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java +++ b/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java @@ -30,7 +30,6 @@ import io.micronaut.security.token.jwt.cookie.JwtCookieLoginHandler; import io.micronaut.security.token.jwt.generator.AccessRefreshTokenGenerator; import io.micronaut.security.token.jwt.generator.AccessTokenConfiguration; -import io.micronaut.security.token.jwt.generator.JwtGeneratorConfiguration; import lombok.extern.slf4j.Slf4j; import org.breedinginsight.api.model.v1.auth.SignUpJWT; import org.breedinginsight.model.ProgramUser; @@ -83,7 +82,7 @@ public AuthServiceLoginHandler(JwtCookieConfiguration jwtCookieConfiguration, @Override public MutableHttpResponse loginSuccess(UserDetails userDetails, HttpRequest request) { - // Called when login to orcid is successful. + // Called when login to OAuth provider is successful. // Check if our login to our system is successful. if (request.getCookies().contains(accountTokenCookieName)) { Cookie accountTokenCookie = request.getCookies().get(accountTokenCookieName); @@ -124,7 +123,7 @@ public MutableHttpResponse loginSuccess(UserDetails userDetails, HttpRequest< private AuthenticatedUser getUserCredentials(UserDetails userDetails) throws AuthenticationException { - Optional user = userService.getByOrcid(userDetails.getUsername()); + Optional user = userService.getByOAuthId(userDetails.getUsername()); if (user.isPresent()) { if (user.get().getActive()) { @@ -161,7 +160,7 @@ public MutableHttpResponse loginFailed(AuthenticationResponse authenticationF private MutableHttpResponse newAccountCreationResponse(UserDetails userDetails, String accountToken, HttpRequest request) { - String orcid = userDetails.getUsername(); + String oAuthId = userDetails.getUsername(); SignUpJWT signUpJWT; try { signUpJWT = signUpJwtService.validateAndParseAccountSignUpJwt(accountToken); @@ -185,9 +184,9 @@ private MutableHttpResponse newAccountCreationResponse(UserDetails userDetails, } if (newUser.getAccountToken().equals(signUpJWT.getJwtId().toString())) { - // Assign orcid to that user + // Assign oAuthId to that user. try { - userService.updateOrcid(newUser.getId(), orcid); + userService.updateOAuthId(newUser.getId(), oAuthId); } catch (DoesNotExistException e) { MutableHttpResponse resp = HttpResponse.seeOther(URI.create(newAccountErrorUrl)); return resp; diff --git a/src/main/java/org/breedinginsight/daos/ProgramUserDAO.java b/src/main/java/org/breedinginsight/daos/ProgramUserDAO.java index 106a34688..94bb9b3a6 100644 --- a/src/main/java/org/breedinginsight/daos/ProgramUserDAO.java +++ b/src/main/java/org/breedinginsight/daos/ProgramUserDAO.java @@ -120,14 +120,14 @@ public List getProgramUsersByUserId(UUID userId) { return parseRecords(records, createdByUser, updatedByUser); } - public List getProgramUsersByOrcid(String orcid) { + public List getProgramUsersByOAuthId(String oAuthId) { BiUserTable createdByUser = BI_USER.as("createdByUser"); BiUserTable updatedByUser = BI_USER.as("updatedByUser"); // TODO: When we allow for pulling archived users, active condition won't be hardcoded. Result records = getProgramUsersQuery(createdByUser, updatedByUser) - .where(BI_USER.ORCID.eq(orcid)) + .where(BI_USER.OAUTH_ID.eq(oAuthId)) .and(PROGRAM.ACTIVE.eq(true)) .fetch(); diff --git a/src/main/java/org/breedinginsight/daos/UserDAO.java b/src/main/java/org/breedinginsight/daos/UserDAO.java index 562801735..a89376982 100644 --- a/src/main/java/org/breedinginsight/daos/UserDAO.java +++ b/src/main/java/org/breedinginsight/daos/UserDAO.java @@ -32,11 +32,11 @@ public interface UserDAO extends DAO { Optional getUser(UUID id); - Optional getUserByOrcId(String orcid); + Optional getUserByOAuthId(String oAuthId); BiUserEntity fetchOneById(UUID value); List fetchByEmail(String... values); - List fetchByOrcid(String... values); + List fetchByOauthId(String... values); } diff --git a/src/main/java/org/breedinginsight/daos/impl/UserDAOImpl.java b/src/main/java/org/breedinginsight/daos/impl/UserDAOImpl.java index e179bc42d..1e6e391b2 100644 --- a/src/main/java/org/breedinginsight/daos/impl/UserDAOImpl.java +++ b/src/main/java/org/breedinginsight/daos/impl/UserDAOImpl.java @@ -67,11 +67,11 @@ public Optional getUser(UUID id) { return Utilities.getSingleOptional(users); } - public Optional getUserByOrcId(String orcid) { + public Optional getUserByOAuthId(String oAuthId) { List records = getUsersQuery() - .where(BI_USER.ORCID.eq(orcid)) + .where(BI_USER.OAUTH_ID.eq(oAuthId)) .fetch(); - List programUsers = programUserDAO.getProgramUsersByOrcid(orcid); + List programUsers = programUserDAO.getProgramUsersByOAuthId(oAuthId); List users = parseRecords(records, programUsers); return Utilities.getSingleOptional(users); diff --git a/src/main/java/org/breedinginsight/model/User.java b/src/main/java/org/breedinginsight/model/User.java index 30386b79d..2ff26f5f5 100644 --- a/src/main/java/org/breedinginsight/model/User.java +++ b/src/main/java/org/breedinginsight/model/User.java @@ -56,13 +56,14 @@ public class User extends BiUserEntity { public User(BiUserEntity biUser) { this.setId(biUser.getId()); - this.setOrcid(biUser.getOrcid()); + this.setOauthId(biUser.getOauthId()); this.setName(biUser.getName()); this.setEmail(biUser.getEmail()); this.setSystemRoles(new ArrayList<>()); this.setProgramRoles(new ArrayList<>()); this.setActive(biUser.getActive()); this.setAccountToken(biUser.getAccountToken()); + this.setOauthProvider(biUser.getOauthProvider()); } public User() { @@ -72,13 +73,14 @@ public User() { public static User parseSQLRecord(Record record, @NotNull BiUserTable tableName){ return User.builder() .id(record.getValue(tableName.ID)) - .orcid(record.getValue(tableName.ORCID)) + .oauthId(record.getValue(tableName.OAUTH_ID)) .name(record.getValue(tableName.NAME)) .email(record.getValue(tableName.EMAIL)) .systemRoles(new ArrayList<>()) .programRoles(new ArrayList<>()) .active(record.getValue(tableName.ACTIVE)) .accountToken(record.getValue(tableName.ACCOUNT_TOKEN)) + .oauthProvider(record.getValue(tableName.OAUTH_PROVIDER)) .build(); } @@ -98,7 +100,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(getId(), user.getId()) && - Objects.equals(getOrcid(), user.getOrcid()) && + Objects.equals(getOauthId(), user.getOauthId()) && Objects.equals(getName(), user.getName()) && Objects.equals(getEmail(), user.getEmail()) && Objects.equals(getCreatedAt(), user.getCreatedAt()) && @@ -106,11 +108,12 @@ public boolean equals(Object o) { Objects.equals(getCreatedBy(), user.getCreatedBy()) && Objects.equals(getUpdatedBy(), user.getUpdatedBy()) && Objects.equals(getActive(), user.getActive()) && - Objects.equals(getAccountToken(), user.getAccountToken()); + Objects.equals(getAccountToken(), user.getAccountToken()) && + Objects.equals(getOauthProvider(), user.getOauthProvider()); } @Override public int hashCode() { - return Objects.hash(getId(), getOrcid(), getName(), getEmail(), getCreatedAt(), getUpdatedAt(), getCreatedBy(), getUpdatedBy(), getActive(), getAccountToken()); + return Objects.hash(getId(), getOauthId(), getName(), getEmail(), getCreatedAt(), getUpdatedAt(), getCreatedBy(), getUpdatedBy(), getActive(), getAccountToken()); } } diff --git a/src/main/java/org/breedinginsight/services/UserService.java b/src/main/java/org/breedinginsight/services/UserService.java index b78fdf8a6..9355e2cf7 100644 --- a/src/main/java/org/breedinginsight/services/UserService.java +++ b/src/main/java/org/breedinginsight/services/UserService.java @@ -91,10 +91,10 @@ public UserService(UserDAO dao, SystemUserRoleDao systemUserRoleDao, SystemRoleD } - public Optional getByOrcid(String orcid) { + public Optional getByOAuthId(String oAuthId) { - // User has been authenticated against orcid, check they have a bi account. - Optional users = dao.getUserByOrcId(orcid); + // User has been authenticated against OAuth provider, check they have a bi account. + Optional users = dao.getUserByOAuthId(oAuthId); if (users.isEmpty()) { return Optional.empty(); @@ -113,7 +113,7 @@ public List getAll() { public Optional getById(UUID userId) { - // User has been authenticated against orcid, check they have a bi account. + // User has been authenticated against OAuth provider, check they have a bi account. Optional user = dao.getUser(userId); if (!user.isPresent()) { @@ -146,7 +146,7 @@ public User create(AuthenticatedUser actingUser, UserRequest userRequest, Config insertSystemRoles(actingUser, newUser.getId(), systemRoles); } - // Start OrcID association flow + // Start OAuth account association flow createAndSendAccountToken(newUser.getId()); return getById(newUser.getId()).get(); @@ -352,7 +352,7 @@ public void createAndSendAccountToken(UUID userId) throws DoesNotExistException sendAccountSignUpEmail(biUser, jwt.getSignedJWT()); } - public void updateOrcid(UUID userId, String orcid) throws DoesNotExistException, AlreadyExistsException { + public void updateOAuthId(UUID userId, String oAuthId) throws DoesNotExistException, AlreadyExistsException { BiUserEntity biUser = dao.fetchOneById(userId); @@ -360,14 +360,14 @@ public void updateOrcid(UUID userId, String orcid) throws DoesNotExistException, throw new DoesNotExistException("UUID for user does not exist"); } - List biUserWithOrcidList = dao.fetchByOrcid(orcid); - for (BiUserEntity biUserWithOrcid: biUserWithOrcidList){ - if (!biUserWithOrcid.getId().equals(userId)){ - throw new AlreadyExistsException("Orcid already in use"); + List biUserWithOAuthIdList = dao.fetchByOauthId(oAuthId); + for (BiUserEntity biUserWithOAuthId: biUserWithOAuthIdList){ + if (!biUserWithOAuthId.getId().equals(userId)){ + throw new AlreadyExistsException("OAuth Id already in use"); } } - biUser.setOrcid(orcid); + biUser.setOauthId(oAuthId); biUser.setAccountToken(null); dao.update(biUser); } diff --git a/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java b/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java index 2eab94b77..5e91ce61e 100644 --- a/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java +++ b/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java @@ -35,7 +35,7 @@ public UserQueryMapper() { fields = Map.ofEntries( Map.entry("name", User::getName), Map.entry("email", User::getEmail), - Map.entry("orcid", User::getOrcid), + Map.entry("orcid", User::getOauthId), Map.entry("systemRoles", user -> user.getSystemRoles() != null ? user.getSystemRoles().stream() .map(role -> role.getDomain()).collect(Collectors.toList()) : null), diff --git a/src/main/resources/db/migration/V1.33.0__rename-orcid.sql b/src/main/resources/db/migration/V1.33.0__rename-orcid.sql new file mode 100644 index 000000000..ac2ad3685 --- /dev/null +++ b/src/main/resources/db/migration/V1.33.0__rename-orcid.sql @@ -0,0 +1,22 @@ +/* + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * Licensed 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. + */ + +ALTER TABLE bi_user +RENAME COLUMN orcid TO oauth_id; + +ALTER TABLE bi_user +ADD COLUMN oauth_provider text; diff --git a/src/test/java/org/breedinginsight/api/auth/rules/ProgramSecuredAnnotationRuleIntegrationTest.java b/src/test/java/org/breedinginsight/api/auth/rules/ProgramSecuredAnnotationRuleIntegrationTest.java index 26a53b9ca..58a35c919 100644 --- a/src/test/java/org/breedinginsight/api/auth/rules/ProgramSecuredAnnotationRuleIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/auth/rules/ProgramSecuredAnnotationRuleIntegrationTest.java @@ -45,11 +45,9 @@ import java.time.OffsetDateTime; import java.util.List; -import java.util.UUID; import static io.micronaut.http.HttpRequest.GET; import static io.micronaut.http.HttpRequest.POST; -import static org.breedinginsight.TestUtils.insertAndFetchTestProgram; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -106,8 +104,8 @@ void setup() { dsl.execute(fp.get("InsertPrograms")); programs = programDAO.getAll(); - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); - otherTestUser = userDAO.getUserByOrcId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(); + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); + otherTestUser = userDAO.getUserByOAuthId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(); // Insert system roles dsl.execute(fp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/BreedingMethodControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/BreedingMethodControllerIntegrationTest.java index 0cf098c04..c3ba499c4 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/BreedingMethodControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/BreedingMethodControllerIntegrationTest.java @@ -90,7 +90,7 @@ void setup() throws Exception { securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java index 9adb44425..e89b0efda 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/ExperimentControllerIntegrationTest.java @@ -113,8 +113,8 @@ void setup() throws Exception { FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); // Test User - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); - otherTestUser = userDAO.getUserByOrcId(TestTokenValidator.OTHER_TEST_USER_ORCID).orElseThrow(Exception::new); + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + otherTestUser = userDAO.getUserByOAuthId(TestTokenValidator.OTHER_TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/ImportControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/ImportControllerIntegrationTest.java index 378e99085..ebd602d3d 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/ImportControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/ImportControllerIntegrationTest.java @@ -109,7 +109,7 @@ public void setup() { validProgram = insertAndFetchTestProgram(program); // Set program user - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), validProgram.getId()); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/InternalServerErrorHandlerUnitTest.java b/src/test/java/org/breedinginsight/api/v1/controller/InternalServerErrorHandlerUnitTest.java index 4fc66e151..42d5d976c 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/InternalServerErrorHandlerUnitTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/InternalServerErrorHandlerUnitTest.java @@ -112,7 +112,7 @@ void setup() { var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); } @BeforeEach diff --git a/src/test/java/org/breedinginsight/api/v1/controller/JobControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/JobControllerIntegrationTest.java index 6949499e9..e964cbb92 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/JobControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/JobControllerIntegrationTest.java @@ -39,7 +39,6 @@ import org.breedinginsight.services.ProgramService; import org.breedinginsight.services.exceptions.DoesNotExistException; import org.jooq.DSLContext; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -95,7 +94,7 @@ public void setup() { program = programs.get(0); // Insert system roles - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID) + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID) .get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId() diff --git a/src/test/java/org/breedinginsight/api/v1/controller/OntologyControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/OntologyControllerIntegrationTest.java index bf8dde632..a8293b5d6 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/OntologyControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/OntologyControllerIntegrationTest.java @@ -72,7 +72,7 @@ void setup() throws Exception { brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); brapiObservationFp = FannyPack.fill("src/test/resources/sql/brapi/BrAPIOntologyControllerIntegrationTest.sql"); - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/ProgramControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/ProgramControllerIntegrationTest.java index b3ef32a2b..32a044fae 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/ProgramControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/ProgramControllerIntegrationTest.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.*; -import com.google.gson.reflect.TypeToken; import io.kowalski.fannypack.FannyPack; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; @@ -42,7 +41,6 @@ import org.breedinginsight.api.model.v1.request.*; import org.breedinginsight.api.model.v1.request.query.FilterRequest; import org.breedinginsight.api.model.v1.request.query.SearchRequest; -import org.breedinginsight.api.model.v1.response.Response; import org.breedinginsight.api.v1.controller.metadata.SortOrder; import org.breedinginsight.dao.db.tables.daos.ProgramDao; import org.breedinginsight.dao.db.tables.pojos.ProgramEntity; @@ -58,7 +56,6 @@ import javax.inject.Inject; import javax.inject.Named; -import java.lang.reflect.Type; import java.math.BigDecimal; import java.time.OffsetDateTime; import java.util.*; @@ -165,13 +162,13 @@ void setup() throws Exception { securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); - otherUser = userDAO.getUserByOrcId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(); + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); + otherUser = userDAO.getUserByOAuthId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); super.getBrapiDsl().execute(brapiFp.get("InsertSpecies")); - Optional optionalUser = userService.getByOrcid(TestTokenValidator.TEST_USER_ORCID); + Optional optionalUser = userService.getByOAuthId(TestTokenValidator.TEST_USER_ORCID); testUser = optionalUser.get(); // Get species for tests @@ -275,7 +272,7 @@ public ProgramLocation insertAndFetchTestLocation() throws Exception { public User fetchTestUser() throws Exception{ - Optional user = userService.getByOrcid(TestTokenValidator.TEST_USER_ORCID); + Optional user = userService.getByOAuthId(TestTokenValidator.TEST_USER_ORCID); if (!user.isPresent()){ throw new Exception("Failed to insert test user"); } diff --git a/src/test/java/org/breedinginsight/api/v1/controller/SampleSubmissionControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/SampleSubmissionControllerIntegrationTest.java index ec28c689b..091d836a3 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/SampleSubmissionControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/SampleSubmissionControllerIntegrationTest.java @@ -48,7 +48,6 @@ import org.breedinginsight.model.*; import org.breedinginsight.services.OntologyService; import org.breedinginsight.services.parsers.ParsingException; -import org.breedinginsight.services.parsers.experiment.ExperimentFileColumns; import org.breedinginsight.services.writers.CSVWriter; import org.breedinginsight.utilities.FileUtil; import org.breedinginsight.utilities.Utilities; @@ -102,7 +101,7 @@ void setup() throws Exception { FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); // Test User - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Species diff --git a/src/test/java/org/breedinginsight/api/v1/controller/TestTokenValidator.java b/src/test/java/org/breedinginsight/api/v1/controller/TestTokenValidator.java index 644ddc046..0e40555ab 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/TestTokenValidator.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/TestTokenValidator.java @@ -53,7 +53,7 @@ public TestTokenValidator(Collection signatureConfigurat public Publisher validateToken(String token) { if (token.equals("test-registered-user")) { - Optional testUser = userService.getByOrcid(TEST_USER_ORCID); + Optional testUser = userService.getByOAuthId(TEST_USER_ORCID); Map adminClaims = new HashMap<>(); List roles = new ArrayList<>(); roles.add("SYSTEM ADMINISTRATOR"); @@ -61,14 +61,14 @@ public Publisher validateToken(String token) { adminClaims.put("id", testUser.get().getId().toString()); return Flowable.just(new DefaultAuthentication(TEST_USER_ORCID, adminClaims)); } else if (token.equals("other-registered-user")) { - Optional otherTestUser = userService.getByOrcid(OTHER_TEST_USER_ORCID); + Optional otherTestUser = userService.getByOAuthId(OTHER_TEST_USER_ORCID); Map userClaims = new HashMap<>(); List roles = new ArrayList<>(); userClaims.put("roles", roles); userClaims.put("id", otherTestUser.get().getId().toString()); return Flowable.just(new DefaultAuthentication(OTHER_TEST_USER_ORCID, userClaims)); } else if (token.equals("another-registered-user")) { - Optional anotherTestUser = userService.getByOrcid(ANOTHER_TEST_USER_ORCID); + Optional anotherTestUser = userService.getByOAuthId(ANOTHER_TEST_USER_ORCID); Map userClaims = new HashMap<>(); List roles = new ArrayList<>(); userClaims.put("roles", roles); @@ -82,7 +82,7 @@ public Publisher validateToken(String token) { adminClaims.put("id", NON_EXISTENT_USER_ID); return Flowable.just(new DefaultAuthentication(NON_EXISTENT_USER_ID, adminClaims)); } else if (token.equals("inactive-user")) { - Optional inactiveUser = userService.getByOrcid(INACTIVE_USER_ORCID); + Optional inactiveUser = userService.getByOAuthId(INACTIVE_USER_ORCID); Map adminClaims = new HashMap<>(); List roles = new ArrayList<>(); roles.add("SYSTEM ADMINISTRATOR"); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/TraitControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/TraitControllerIntegrationTest.java index ebfeec7ed..08c7edd37 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/TraitControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/TraitControllerIntegrationTest.java @@ -160,7 +160,7 @@ public void setup() { var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Insert program diff --git a/src/test/java/org/breedinginsight/api/v1/controller/UploadControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/UploadControllerIntegrationTest.java index 6549586e0..b008ccf51 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/UploadControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/UploadControllerIntegrationTest.java @@ -107,7 +107,7 @@ public void setup() { validProgram = insertAndFetchTestProgram(program); // Set program user - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), validProgram.getId()); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java index 23fcfa32c..45774b5ea 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java @@ -110,8 +110,8 @@ void setup() { dsl.execute(fp.get("InsertProgram")); dsl.execute(fp.get("InsertUserProgramAssociations")); - testUser = biUserDao.fetchByOrcid(TestTokenValidator.TEST_USER_ORCID).get(0); - otherTestUser = biUserDao.fetchByOrcid(TestTokenValidator.OTHER_TEST_USER_ORCID).get(0); + testUser = biUserDao.fetchByOauthId(TestTokenValidator.TEST_USER_ORCID).get(0); + otherTestUser = biUserDao.fetchByOauthId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(0); validSystemRole = systemRoleDao.findAll().get(0); validRole = roleDao.findAll().get(0); validPrograms = programDao.findAll(); @@ -144,7 +144,7 @@ public void getUsersExistingId() { JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); assertEquals(testUser.getId().toString(), result.get("id").getAsString(), "Wrong id"); assertEquals("Test User", result.get("name").getAsString(), "Wrong name"); - assertEquals(testUser.getOrcid(), result.get("orcid").getAsString(), "Wrong orcid"); + assertEquals(testUser.getOauthId(), result.get("orcid").getAsString(), "Wrong orcid"); assertEquals("test@test.com", result.get("email").getAsString(), "Wrong email"); JsonArray resultRoles = (JsonArray) result.get("systemRoles"); @@ -584,7 +584,7 @@ public void getUserInfoRegisteredUser() { JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); assertEquals(otherTestUser.getName(), result.get("name").getAsString(), "Wrong name"); - assertEquals(otherTestUser.getOrcid(), result.get("orcid").getAsString(), "Wrong orcid"); + assertEquals(otherTestUser.getOauthId(), result.get("orcid").getAsString(), "Wrong orcid"); assertEquals(otherTestUser.getEmail(), result.get("email").getAsString(), "Wrong email"); assertEquals(otherTestUser.getId().toString(), result.get("id").getAsString(), "Wrong id"); diff --git a/src/test/java/org/breedinginsight/api/v1/controller/brapi/BrAPIServiceFilterIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/brapi/BrAPIServiceFilterIntegrationTest.java index bd565374e..0fbfe4035 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/brapi/BrAPIServiceFilterIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/brapi/BrAPIServiceFilterIntegrationTest.java @@ -181,7 +181,7 @@ public void insertTestData() { var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Insert program diff --git a/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java index 4257f90d6..4f04c3578 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/metadata/MetadataFilterIntegrationTest.java @@ -84,8 +84,8 @@ public void setup() { dsl.execute(fp.get("InsertUserProgramAssociations")); dsl.execute(fp.get("InsertManyUsers")); - testUser = biUserDao.fetchByOrcid(TestTokenValidator.TEST_USER_ORCID).get(0); - otherTestUser = biUserDao.fetchByOrcid(TestTokenValidator.OTHER_TEST_USER_ORCID).get(0); + testUser = biUserDao.fetchByOauthId(TestTokenValidator.TEST_USER_ORCID).get(0); + otherTestUser = biUserDao.fetchByOauthId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(0); dsl.execute(fp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); dsl.execute(fp.get("InsertSystemRoleAdmin"), otherTestUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiAuthorizeControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiAuthorizeControllerIntegrationTest.java index 861bf27db..5567f7db8 100644 --- a/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiAuthorizeControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiAuthorizeControllerIntegrationTest.java @@ -67,7 +67,7 @@ public void setup() { var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Insert program diff --git a/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiV1ObservationVariablesControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiV1ObservationVariablesControllerIntegrationTest.java index 038a691ec..9173d3cdf 100644 --- a/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiV1ObservationVariablesControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v1/controller/BrapiV1ObservationVariablesControllerIntegrationTest.java @@ -105,8 +105,8 @@ public void setup() { var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); - User otherTestUser = userDAO.getUserByOrcId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); + User otherTestUser = userDAO.getUserByOAuthId(TestTokenValidator.OTHER_TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Insert program diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java index 61ce61a57..4f15c553d 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationLevelsControllerIntegrationTest.java @@ -106,7 +106,7 @@ void setup() throws Exception { FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); // Test User - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Species diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java index 6bea444f9..44792ea64 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationUnitControllerIntegrationTest.java @@ -110,7 +110,7 @@ void setup() throws Exception { FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); // Test User - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Species diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java index 27cf6defd..59cd33630 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIObservationsControllerIntegrationTest.java @@ -111,7 +111,7 @@ void setup() throws Exception { FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); // Test User - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Species diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPITestUtils.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPITestUtils.java index d0b440f79..936bd401c 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPITestUtils.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPITestUtils.java @@ -96,7 +96,7 @@ public Tuple2> setupTestProgram(DSLContext brAPIDslContext FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Test User - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Species @@ -121,7 +121,7 @@ public Tuple2> setupTestProgram(DSLContext brAPIDslContext dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), program.getId()); // Add Experimental Collaborator user. - User collaborator = userDAO.getUserByOrcId(TestTokenValidator.OTHER_TEST_USER_ORCID).orElseThrow(Exception::new); + User collaborator = userDAO.getUserByOAuthId(TestTokenValidator.OTHER_TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertProgramRolesExperimentalCollaborator"), collaborator.getId().toString(), program.getId().toString()); // Get experiment import map @@ -208,7 +208,7 @@ public Tuple2> setupTestProgram(DSLContext brAPIDslContext .get("id").getAsString(); // Refetch collaborator, since we updated program_user_roles. - collaborator = userDAO.getUserByOrcId(TestTokenValidator.OTHER_TEST_USER_ORCID).orElseThrow(Exception::new); + collaborator = userDAO.getUserByOAuthId(TestTokenValidator.OTHER_TEST_USER_ORCID).orElseThrow(Exception::new); // Add collaborator to experiment. dsl.execute(securityFp.get("AddCollaborator"), experiment2Id, collaborator.getProgramRoles().get(0).getId()); diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java index 1e1cb922b..24868f3fe 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java @@ -89,7 +89,7 @@ public void setup() { var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID) + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID) .get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId() diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java index 42dfa47c2..9fc0aac90 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ObservationVariableControllerIntegrationTest.java @@ -108,7 +108,7 @@ void setup() throws Exception { FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); // Test User - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Species diff --git a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java index de42ac99f..9b9cda353 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/GermplasmControllerIntegrationTest.java @@ -87,7 +87,7 @@ public void setup() { var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); var brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); - testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Set up BrAPI diff --git a/src/test/java/org/breedinginsight/brapps/importer/ImportTestUtils.java b/src/test/java/org/breedinginsight/brapps/importer/ImportTestUtils.java index 53abd8483..2d89d5110 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/ImportTestUtils.java +++ b/src/test/java/org/breedinginsight/brapps/importer/ImportTestUtils.java @@ -180,7 +180,7 @@ public Map setup(RxHttpClient client, Gson gson, DSLContext dsl, .getAsJsonArray("data") .get(0).getAsJsonObject().get("id").getAsString(); - BiUserEntity testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + BiUserEntity testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertProgramRolesBreeder"), testUser.getId().toString(), validProgram.getId()); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); diff --git a/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java b/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java index c4c214682..f4375e590 100644 --- a/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java +++ b/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java @@ -92,7 +92,7 @@ public void setup() { // Insert system admin role so can create program FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); SpeciesRequest speciesRequest = SpeciesRequest.builder() diff --git a/src/test/java/org/breedinginsight/daos/DSLTransactionResultIntegrationTest.java b/src/test/java/org/breedinginsight/daos/DSLTransactionResultIntegrationTest.java index a298361fa..e0cd9bb31 100644 --- a/src/test/java/org/breedinginsight/daos/DSLTransactionResultIntegrationTest.java +++ b/src/test/java/org/breedinginsight/daos/DSLTransactionResultIntegrationTest.java @@ -28,7 +28,6 @@ import org.jooq.DSLContext; import org.jooq.exception.DataAccessException; import org.jooq.impl.DSL; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -60,7 +59,7 @@ public class DSLTransactionResultIntegrationTest extends DatabaseTest { @BeforeAll void setup() throws Exception { - Optional userOptional = userService.getByOrcid(TestTokenValidator.TEST_USER_ORCID); + Optional userOptional = userService.getByOAuthId(TestTokenValidator.TEST_USER_ORCID); actingUser = userOptional.get(); } diff --git a/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java b/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java index 3006bc7b5..b31839ff2 100644 --- a/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java +++ b/src/test/java/org/breedinginsight/services/validators/TraitValidatorIntegrationTest.java @@ -86,7 +86,7 @@ public void setup() throws Exception { FannyPack brapiFp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); // Test User - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).orElseThrow(Exception::new); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Species diff --git a/src/test/java/org/breedinginsight/utilities/response/ResponseUtilsIntegrationTest.java b/src/test/java/org/breedinginsight/utilities/response/ResponseUtilsIntegrationTest.java index 7cd16622a..56f977a47 100644 --- a/src/test/java/org/breedinginsight/utilities/response/ResponseUtilsIntegrationTest.java +++ b/src/test/java/org/breedinginsight/utilities/response/ResponseUtilsIntegrationTest.java @@ -93,7 +93,7 @@ public void setup() throws MissingRequiredInfoException, UnprocessableEntityExce var securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); // Insert system roles - User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + User testUser = userDAO.getUserByOAuthId(TestTokenValidator.TEST_USER_ORCID).get(); dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); // Insert program diff --git a/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java b/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java index 8e5be117d..daedfa20a 100644 --- a/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java +++ b/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java @@ -48,7 +48,7 @@ public void testMappings() { User user = User.builder() .name("Test User") .email("test@user.com") - .orcid("000000-000000-000000-00000") + .oauthId("000000-000000-000000-00000") .systemRoles(List.of(SystemRole.builder().domain("System Administrator").build())) .programRoles(List.of(ProgramUser.builder().program(Program.builder().name("Test program").build()).build())) .active(false) @@ -60,7 +60,7 @@ public void testMappings() { assertEquals(user.getName(), userQueryMapper.getField("name").apply(user), "Wrong getter"); assertEquals(user.getEmail(), userQueryMapper.getField("email").apply(user), "Wrong getter"); - assertEquals(user.getOrcid(), userQueryMapper.getField("orcid").apply(user), "Wrong getter"); + assertEquals(user.getOauthId(), userQueryMapper.getField("orcid").apply(user), "Wrong getter"); assertEquals(user.getSystemRoles().stream().map(role -> role.getDomain()).collect(Collectors.toList()), userQueryMapper.getField("systemRoles").apply(user), "Wrong getter"); assertEquals(user.getProgramRoles().stream().map(role -> role.getProgram().getName()).collect(Collectors.toList()), From 61dc2b1927aef4514b92ee9cebc05d832944ac93 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:34:39 -0400 Subject: [PATCH 03/14] [BI-2540] - disabled flyway out-of-order --- src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 76cf54b28..616747556 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -122,7 +122,7 @@ flyway: default-url: ${brapi.server.default-url} brapi-reference-source: ${brapi.server.reference-source} orcid-sandbox-authentication: ${ORCID_SANDBOX_AUTHENTICATION:false} - out-of-order: true + out-of-order: false jooq: datasources: default: From d3c0508c51b3fdfbc5f374a6f2a9de516fd3d507 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:51:45 -0400 Subject: [PATCH 04/14] [BI-2540] - utilized oauth_provider column --- .../api/auth/AuthServiceLoginHandler.java | 15 +++++++++++++-- src/main/java/org/breedinginsight/model/User.java | 2 +- .../org/breedinginsight/services/UserService.java | 3 ++- .../response/mappers/UserQueryMapper.java | 2 +- .../db/migration/V1.33.0__rename-orcid.sql | 4 +++- .../controller/UserControllerIntegrationTest.java | 8 ++++---- .../response/mappers/UserQueryMapperUnitTest.java | 2 +- 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java b/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java index 8e210f608..a03b21641 100644 --- a/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java +++ b/src/main/java/org/breedinginsight/api/auth/AuthServiceLoginHandler.java @@ -158,9 +158,20 @@ public MutableHttpResponse loginFailed(AuthenticationResponse authenticationF } } + private String parseOAuthProvider(HttpRequest request) { + // The request path will be something like "/sso/success/github". + if (request.getPath().toLowerCase().contains("github")) { + return "github"; + } else { + // Default to ORCID. + return "orcid"; + } + } + private MutableHttpResponse newAccountCreationResponse(UserDetails userDetails, String accountToken, HttpRequest request) { String oAuthId = userDetails.getUsername(); + String oAuthProvider = parseOAuthProvider(request); SignUpJWT signUpJWT; try { signUpJWT = signUpJwtService.validateAndParseAccountSignUpJwt(accountToken); @@ -184,9 +195,9 @@ private MutableHttpResponse newAccountCreationResponse(UserDetails userDetails, } if (newUser.getAccountToken().equals(signUpJWT.getJwtId().toString())) { - // Assign oAuthId to that user. + // Assign OAuth Id and provider to that user. try { - userService.updateOAuthId(newUser.getId(), oAuthId); + userService.updateOAuthInfo(newUser.getId(), oAuthId, oAuthProvider); } catch (DoesNotExistException e) { MutableHttpResponse resp = HttpResponse.seeOther(URI.create(newAccountErrorUrl)); return resp; diff --git a/src/main/java/org/breedinginsight/model/User.java b/src/main/java/org/breedinginsight/model/User.java index 2ff26f5f5..9d72b1841 100644 --- a/src/main/java/org/breedinginsight/model/User.java +++ b/src/main/java/org/breedinginsight/model/User.java @@ -114,6 +114,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(getId(), getOauthId(), getName(), getEmail(), getCreatedAt(), getUpdatedAt(), getCreatedBy(), getUpdatedBy(), getActive(), getAccountToken()); + return Objects.hash(getId(), getOauthId(), getName(), getEmail(), getCreatedAt(), getUpdatedAt(), getCreatedBy(), getUpdatedBy(), getActive(), getAccountToken(), getOauthProvider()); } } diff --git a/src/main/java/org/breedinginsight/services/UserService.java b/src/main/java/org/breedinginsight/services/UserService.java index 9355e2cf7..33c459254 100644 --- a/src/main/java/org/breedinginsight/services/UserService.java +++ b/src/main/java/org/breedinginsight/services/UserService.java @@ -352,7 +352,7 @@ public void createAndSendAccountToken(UUID userId) throws DoesNotExistException sendAccountSignUpEmail(biUser, jwt.getSignedJWT()); } - public void updateOAuthId(UUID userId, String oAuthId) throws DoesNotExistException, AlreadyExistsException { + public void updateOAuthInfo(UUID userId, String oAuthId, String oAuthProvider) throws DoesNotExistException, AlreadyExistsException { BiUserEntity biUser = dao.fetchOneById(userId); @@ -368,6 +368,7 @@ public void updateOAuthId(UUID userId, String oAuthId) throws DoesNotExistExcept } biUser.setOauthId(oAuthId); + biUser.setOauthProvider(oAuthProvider); biUser.setAccountToken(null); dao.update(biUser); } diff --git a/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java b/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java index 5e91ce61e..c1ba7f0c6 100644 --- a/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java +++ b/src/main/java/org/breedinginsight/utilities/response/mappers/UserQueryMapper.java @@ -35,7 +35,7 @@ public UserQueryMapper() { fields = Map.ofEntries( Map.entry("name", User::getName), Map.entry("email", User::getEmail), - Map.entry("orcid", User::getOauthId), + Map.entry("oauthId", User::getOauthId), Map.entry("systemRoles", user -> user.getSystemRoles() != null ? user.getSystemRoles().stream() .map(role -> role.getDomain()).collect(Collectors.toList()) : null), diff --git a/src/main/resources/db/migration/V1.33.0__rename-orcid.sql b/src/main/resources/db/migration/V1.33.0__rename-orcid.sql index ac2ad3685..928a426fa 100644 --- a/src/main/resources/db/migration/V1.33.0__rename-orcid.sql +++ b/src/main/resources/db/migration/V1.33.0__rename-orcid.sql @@ -15,8 +15,10 @@ * limitations under the License. */ +-- Rename orcid column to more generic oauth_id. ALTER TABLE bi_user RENAME COLUMN orcid TO oauth_id; +-- Add a column to store the OAuth provider with 'orcid' as the default value. ALTER TABLE bi_user -ADD COLUMN oauth_provider text; +ADD COLUMN oauth_provider text DEFAULT 'orcid'; diff --git a/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java b/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java index 45774b5ea..4914824f3 100644 --- a/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/api/v1/controller/UserControllerIntegrationTest.java @@ -144,7 +144,7 @@ public void getUsersExistingId() { JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); assertEquals(testUser.getId().toString(), result.get("id").getAsString(), "Wrong id"); assertEquals("Test User", result.get("name").getAsString(), "Wrong name"); - assertEquals(testUser.getOauthId(), result.get("orcid").getAsString(), "Wrong orcid"); + assertEquals(testUser.getOauthId(), result.get("oauthId").getAsString(), "Wrong OAuthId"); assertEquals("test@test.com", result.get("email").getAsString(), "Wrong email"); JsonArray resultRoles = (JsonArray) result.get("systemRoles"); @@ -203,12 +203,12 @@ void postUsersRolesNotFound() { String name = "Test User4"; String email = "test4@test.com"; - String orcid = "0000-0000-0000-0000"; + String oAuthId = "0000-0000-0000-0000"; JsonObject requestBody = new JsonObject(); requestBody.addProperty("name", name); requestBody.addProperty("email", email); - requestBody.addProperty("orcid", orcid); + requestBody.addProperty("oauthId", oAuthId); JsonObject role = new JsonObject(); role.addProperty("id", invalidUUID); JsonArray roles = new JsonArray(); @@ -584,7 +584,7 @@ public void getUserInfoRegisteredUser() { JsonObject result = JsonParser.parseString(response.body()).getAsJsonObject().getAsJsonObject("result"); assertEquals(otherTestUser.getName(), result.get("name").getAsString(), "Wrong name"); - assertEquals(otherTestUser.getOauthId(), result.get("orcid").getAsString(), "Wrong orcid"); + assertEquals(otherTestUser.getOauthId(), result.get("oauthId").getAsString(), "Wrong orcid"); assertEquals(otherTestUser.getEmail(), result.get("email").getAsString(), "Wrong email"); assertEquals(otherTestUser.getId().toString(), result.get("id").getAsString(), "Wrong id"); diff --git a/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java b/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java index daedfa20a..50545644b 100644 --- a/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java +++ b/src/test/java/org/breedinginsight/utilities/response/mappers/UserQueryMapperUnitTest.java @@ -60,7 +60,7 @@ public void testMappings() { assertEquals(user.getName(), userQueryMapper.getField("name").apply(user), "Wrong getter"); assertEquals(user.getEmail(), userQueryMapper.getField("email").apply(user), "Wrong getter"); - assertEquals(user.getOauthId(), userQueryMapper.getField("orcid").apply(user), "Wrong getter"); + assertEquals(user.getOauthId(), userQueryMapper.getField("oauthId").apply(user), "Wrong getter"); assertEquals(user.getSystemRoles().stream().map(role -> role.getDomain()).collect(Collectors.toList()), userQueryMapper.getField("systemRoles").apply(user), "Wrong getter"); assertEquals(user.getProgramRoles().stream().map(role -> role.getProgram().getName()).collect(Collectors.toList()), From 1556ca36bfbd24a506b51ee1b413b87445a52a5e Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Fri, 27 Jun 2025 14:07:25 -0400 Subject: [PATCH 05/14] [BI-2540] - updated test SQL --- .../resources/sql/ExperimentalCollaboratorServiceTest.sql | 4 ++-- src/test/resources/sql/JobControllerIntegrationTest.sql | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/resources/sql/ExperimentalCollaboratorServiceTest.sql b/src/test/resources/sql/ExperimentalCollaboratorServiceTest.sql index b1b1a26c9..10f361cf4 100644 --- a/src/test/resources/sql/ExperimentalCollaboratorServiceTest.sql +++ b/src/test/resources/sql/ExperimentalCollaboratorServiceTest.sql @@ -17,8 +17,8 @@ */ -- name: CreateUser -INSERT INTO bi_user (id, orcid, name, email, created_by, updated_by, active) -VALUES ('594ec70e-0476-4c40-baf5-581ab0cfcd75', '0000-0001-2345-6789', 'Tester', 'tester@mailinator.com', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000', true); +INSERT INTO bi_user (id, oauth_id, name, email, created_by, updated_by, active, oauth_provider) +VALUES ('594ec70e-0476-4c40-baf5-581ab0cfcd75', '0000-0001-2345-6789', 'Tester', 'tester@mailinator.com', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000', true, 'orcid'); -- name: CreateProgram INSERT INTO program (id, species_id, name, created_by, updated_by, active, key, germplasm_sequence, exp_sequence, env_sequence) diff --git a/src/test/resources/sql/JobControllerIntegrationTest.sql b/src/test/resources/sql/JobControllerIntegrationTest.sql index 596c44bda..d238741eb 100644 --- a/src/test/resources/sql/JobControllerIntegrationTest.sql +++ b/src/test/resources/sql/JobControllerIntegrationTest.sql @@ -18,12 +18,12 @@ -- name: InsertJobs -INSERT INTO public.importer_progress (id, statuscode, message, body, total, finished, in_progress, created_at, updated_at, created_by, updated_by) VALUES ('d02090c3-e40d-45f4-9a63-b8c701c9531b', 202, 'Uploading to brapi service', null, 500, 100, 400, '2022-05-05 21:42:42 +00:00', '2022-05-05 21:42:42 +00:00', (select id from bi_user where orcid = '1111-2222-3333-4444'), (select id from bi_user where orcid = '1111-2222-3333-4444')); -INSERT INTO public.importer_progress (id, statuscode, message, body, total, finished, in_progress, created_at, updated_at, created_by, updated_by) VALUES ('066a37e4-c2e7-4d04-9175-7164c7dce70f', 200, 'Completed upload to brapi service', null, 17, 1, 0, '2022-05-17 14:19:33 +00:00', '2022-05-17 14:19:33 +00:00', (select id from bi_user where orcid = '1111-2222-3333-4444'), (select id from bi_user where orcid = '1111-2222-3333-4444')); +INSERT INTO public.importer_progress (id, statuscode, message, body, total, finished, in_progress, created_at, updated_at, created_by, updated_by) VALUES ('d02090c3-e40d-45f4-9a63-b8c701c9531b', 202, 'Uploading to brapi service', null, 500, 100, 400, '2022-05-05 21:42:42 +00:00', '2022-05-05 21:42:42 +00:00', (select id from bi_user where oauth_id = '1111-2222-3333-4444'), (select id from bi_user where oauth_id = '1111-2222-3333-4444')); +INSERT INTO public.importer_progress (id, statuscode, message, body, total, finished, in_progress, created_at, updated_at, created_by, updated_by) VALUES ('066a37e4-c2e7-4d04-9175-7164c7dce70f', 200, 'Completed upload to brapi service', null, 17, 1, 0, '2022-05-17 14:19:33 +00:00', '2022-05-17 14:19:33 +00:00', (select id from bi_user where oauth_id = '1111-2222-3333-4444'), (select id from bi_user where oauth_id = '1111-2222-3333-4444')); -INSERT INTO public.importer_import (id, program_id, user_id, importer_mapping_id, importer_progress_id, upload_file_name, file_data, modified_data, mapped_data, created_at, updated_at, created_by, updated_by) VALUES ('9aeeace2-7370-4b7d-a574-0ea799a342ef', ?::uuid, (select id from bi_user where orcid = '1111-2222-3333-4444'), (select id from importer_mapping where name = 'GermplasmTemplateMap'), '066a37e4-c2e7-4d04-9175-7164c7dce70f', 'germ import 2.xlsx', '[{"Name": "Gnome-1", "Source": "USDA", "Entry No": "1", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-2", "Source": "USDA", "Entry No": "2", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3", "Source": "Breeding Insight", "Entry No": "3", "External UID": "", "Breeding Method": "BPC", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "2", "Female Parent Entry No": "1"}, {"Name": "Gnome-3-1", "Source": "Breeding Insight", "Entry No": "4", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-2", "Source": "Breeding Insight", "Entry No": "5", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-3", "Source": "Breeding Insight", "Entry No": "6", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-4", "Source": "Breeding Insight", "Entry No": "7", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-5", "Source": "Breeding Insight", "Entry No": "8", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-6", "Source": "Breeding Insight", "Entry No": "9", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-7", "Source": "Breeding Insight", "Entry No": "10", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "11", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "9"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "12", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "3"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "13", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "7"}]', null, '{"rows": [{"germplasm": {"id": "1580b261-f504-4a7e-bbc7-e5599f0b2f4b", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-1 [MNTTIM-1]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "1"}, "commonCropName": "test", "accessionNumber": "1", "defaultDisplayName": "Gnome-1", "externalReferences": [{"referenceID": "6a1269cb-6e7a-4b87-a353-c681c214bd69", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "7e665eb0-2f7b-4b14-85a4-50f5508de2a8", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-2 [MNTTIM-2]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "2"}, "commonCropName": "test", "accessionNumber": "2", "defaultDisplayName": "Gnome-2", "externalReferences": [{"referenceID": "bcc3fab6-04ed-4e2f-b0b0-9e9b247462ef", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "eddb03ff-09c5-481d-9c1c-8fc609d432e5", "state": "NEW", "brAPIObject": {"pedigree": "Gnome-1 [MNTTIM-1]/Gnome-2 [MNTTIM-2]", "seedSource": "Breeding Insight", "germplasmName": "Gnome-3 [MNTTIM-3]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Biparental cross", "breedingMethodId": "aee4d3a3-0df8-422f-b052-d9f0b81e5b4d", "importEntryNumber": "3", "maleParentEntryNo": "2", "femaleParentEntryNo": "1"}, "commonCropName": "test", "accessionNumber": "3", "defaultDisplayName": "Gnome-3", "externalReferences": [{"referenceID": "24c3164e-0b37-44b4-8b18-9118ca9b2949", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "42ff9b3e-3f04-42f3-909b-2aaa158457e5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-1 [MNTTIM-4]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "4"}, "commonCropName": "test", "accessionNumber": "4", "defaultDisplayName": "Gnome-3-1", "externalReferences": [{"referenceID": "c271722b-488b-41c8-9671-73ac008be522", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "883cef8e-34c9-43f7-8062-d7d80c8a65fd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-2 [MNTTIM-5]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "5"}, "commonCropName": "test", "accessionNumber": "5", "defaultDisplayName": "Gnome-3-2", "externalReferences": [{"referenceID": "6bb014ce-0c5f-480c-a1e2-9b87a914a733", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "6215c013-2ed9-43c1-a964-15f87024da36", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-3 [MNTTIM-6]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "6"}, "commonCropName": "test", "accessionNumber": "6", "defaultDisplayName": "Gnome-3-3", "externalReferences": [{"referenceID": "988fe16a-6308-4146-9cae-be9d0cd9c295", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "bd62dec0-25d3-4bef-b9f7-5af634f78366", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-4 [MNTTIM-7]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "7"}, "commonCropName": "test", "accessionNumber": "7", "defaultDisplayName": "Gnome-3-4", "externalReferences": [{"referenceID": "28e6482a-2804-4aa8-b98d-6c4a56f8d8d2", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "c0067078-7a6d-4c99-b6ee-ab220feccddd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-5 [MNTTIM-8]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "8"}, "commonCropName": "test", "accessionNumber": "8", "defaultDisplayName": "Gnome-3-5", "externalReferences": [{"referenceID": "e7c25b55-61f5-4845-9080-d2243171f70b", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "cdb38aac-51fc-4ea7-9b38-8072258ceac5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-6 [MNTTIM-9]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "9"}, "commonCropName": "test", "accessionNumber": "9", "defaultDisplayName": "Gnome-3-6", "externalReferences": [{"referenceID": "ade6b8ca-b2e0-49cc-ac09-09476b1416e7", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "3f6c7d4b-fca2-4b22-aa6a-aadb347cd625", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-7 [MNTTIM-10]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "10"}, "commonCropName": "test", "accessionNumber": "10", "defaultDisplayName": "Gnome-3-7", "externalReferences": [{"referenceID": "6cd34ea3-86b0-41a2-aef0-57c101bd68fb", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "68a72f81-42b5-4425-8034-eef7d6921928", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-6 [MNTTIM-9]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-11]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "11", "maleParentEntryNo": "1", "femaleParentEntryNo": "9"}, "commonCropName": "test", "accessionNumber": "11", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "354faf95-682c-4a62-90f4-4f6c9c2d4191", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "25d84084-45a6-4d93-a683-004f95d3dc3e", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3 [MNTTIM-3]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-12]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "12", "maleParentEntryNo": "1", "femaleParentEntryNo": "3"}, "commonCropName": "test", "accessionNumber": "12", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "936a608f-0e2b-4cee-b348-dbbd57cbdf65", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "569fd3a0-1960-45e0-86d4-5157e21e8130", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-4 [MNTTIM-7]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-13]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "13", "maleParentEntryNo": "1", "femaleParentEntryNo": "7"}, "commonCropName": "test", "accessionNumber": "13", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "4e708554-e088-48ce-a16d-43f1778549fa", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}], "statistics": {"Germplasm": {"newObjectCount": 13}, "Pedigree Connections": {"newObjectCount": 4}}}', '2022-05-17 14:19:33 +00:00', '2022-05-17 14:19:33 +00:00', (select id from bi_user where orcid = '1111-2222-3333-4444'), (select id from bi_user where orcid = '1111-2222-3333-4444')); +INSERT INTO public.importer_import (id, program_id, user_id, importer_mapping_id, importer_progress_id, upload_file_name, file_data, modified_data, mapped_data, created_at, updated_at, created_by, updated_by) VALUES ('9aeeace2-7370-4b7d-a574-0ea799a342ef', ?::uuid, (select id from bi_user where oauth_id = '1111-2222-3333-4444'), (select id from importer_mapping where name = 'GermplasmTemplateMap'), '066a37e4-c2e7-4d04-9175-7164c7dce70f', 'germ import 2.xlsx', '[{"Name": "Gnome-1", "Source": "USDA", "Entry No": "1", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-2", "Source": "USDA", "Entry No": "2", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3", "Source": "Breeding Insight", "Entry No": "3", "External UID": "", "Breeding Method": "BPC", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "2", "Female Parent Entry No": "1"}, {"Name": "Gnome-3-1", "Source": "Breeding Insight", "Entry No": "4", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-2", "Source": "Breeding Insight", "Entry No": "5", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-3", "Source": "Breeding Insight", "Entry No": "6", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-4", "Source": "Breeding Insight", "Entry No": "7", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-5", "Source": "Breeding Insight", "Entry No": "8", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-6", "Source": "Breeding Insight", "Entry No": "9", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-7", "Source": "Breeding Insight", "Entry No": "10", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "11", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "9"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "12", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "3"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "13", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "7"}]', null, '{"rows": [{"germplasm": {"id": "1580b261-f504-4a7e-bbc7-e5599f0b2f4b", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-1 [MNTTIM-1]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "1"}, "commonCropName": "test", "accessionNumber": "1", "defaultDisplayName": "Gnome-1", "externalReferences": [{"referenceID": "6a1269cb-6e7a-4b87-a353-c681c214bd69", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "7e665eb0-2f7b-4b14-85a4-50f5508de2a8", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-2 [MNTTIM-2]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "2"}, "commonCropName": "test", "accessionNumber": "2", "defaultDisplayName": "Gnome-2", "externalReferences": [{"referenceID": "bcc3fab6-04ed-4e2f-b0b0-9e9b247462ef", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "eddb03ff-09c5-481d-9c1c-8fc609d432e5", "state": "NEW", "brAPIObject": {"pedigree": "Gnome-1 [MNTTIM-1]/Gnome-2 [MNTTIM-2]", "seedSource": "Breeding Insight", "germplasmName": "Gnome-3 [MNTTIM-3]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Biparental cross", "breedingMethodId": "aee4d3a3-0df8-422f-b052-d9f0b81e5b4d", "importEntryNumber": "3", "maleParentEntryNo": "2", "femaleParentEntryNo": "1"}, "commonCropName": "test", "accessionNumber": "3", "defaultDisplayName": "Gnome-3", "externalReferences": [{"referenceID": "24c3164e-0b37-44b4-8b18-9118ca9b2949", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "42ff9b3e-3f04-42f3-909b-2aaa158457e5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-1 [MNTTIM-4]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "4"}, "commonCropName": "test", "accessionNumber": "4", "defaultDisplayName": "Gnome-3-1", "externalReferences": [{"referenceID": "c271722b-488b-41c8-9671-73ac008be522", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "883cef8e-34c9-43f7-8062-d7d80c8a65fd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-2 [MNTTIM-5]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "5"}, "commonCropName": "test", "accessionNumber": "5", "defaultDisplayName": "Gnome-3-2", "externalReferences": [{"referenceID": "6bb014ce-0c5f-480c-a1e2-9b87a914a733", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "6215c013-2ed9-43c1-a964-15f87024da36", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-3 [MNTTIM-6]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "6"}, "commonCropName": "test", "accessionNumber": "6", "defaultDisplayName": "Gnome-3-3", "externalReferences": [{"referenceID": "988fe16a-6308-4146-9cae-be9d0cd9c295", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "bd62dec0-25d3-4bef-b9f7-5af634f78366", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-4 [MNTTIM-7]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "7"}, "commonCropName": "test", "accessionNumber": "7", "defaultDisplayName": "Gnome-3-4", "externalReferences": [{"referenceID": "28e6482a-2804-4aa8-b98d-6c4a56f8d8d2", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "c0067078-7a6d-4c99-b6ee-ab220feccddd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-5 [MNTTIM-8]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "8"}, "commonCropName": "test", "accessionNumber": "8", "defaultDisplayName": "Gnome-3-5", "externalReferences": [{"referenceID": "e7c25b55-61f5-4845-9080-d2243171f70b", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "cdb38aac-51fc-4ea7-9b38-8072258ceac5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-6 [MNTTIM-9]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "9"}, "commonCropName": "test", "accessionNumber": "9", "defaultDisplayName": "Gnome-3-6", "externalReferences": [{"referenceID": "ade6b8ca-b2e0-49cc-ac09-09476b1416e7", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "3f6c7d4b-fca2-4b22-aa6a-aadb347cd625", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-7 [MNTTIM-10]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "10"}, "commonCropName": "test", "accessionNumber": "10", "defaultDisplayName": "Gnome-3-7", "externalReferences": [{"referenceID": "6cd34ea3-86b0-41a2-aef0-57c101bd68fb", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "68a72f81-42b5-4425-8034-eef7d6921928", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-6 [MNTTIM-9]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-11]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "11", "maleParentEntryNo": "1", "femaleParentEntryNo": "9"}, "commonCropName": "test", "accessionNumber": "11", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "354faf95-682c-4a62-90f4-4f6c9c2d4191", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "25d84084-45a6-4d93-a683-004f95d3dc3e", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3 [MNTTIM-3]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-12]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "12", "maleParentEntryNo": "1", "femaleParentEntryNo": "3"}, "commonCropName": "test", "accessionNumber": "12", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "936a608f-0e2b-4cee-b348-dbbd57cbdf65", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "569fd3a0-1960-45e0-86d4-5157e21e8130", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-4 [MNTTIM-7]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-13]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "13", "maleParentEntryNo": "1", "femaleParentEntryNo": "7"}, "commonCropName": "test", "accessionNumber": "13", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "4e708554-e088-48ce-a16d-43f1778549fa", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}], "statistics": {"Germplasm": {"newObjectCount": 13}, "Pedigree Connections": {"newObjectCount": 4}}}', '2022-05-17 14:19:33 +00:00', '2022-05-17 14:19:33 +00:00', (select id from bi_user where oauth_id = '1111-2222-3333-4444'), (select id from bi_user where oauth_id = '1111-2222-3333-4444')); -INSERT INTO public.importer_import (id, program_id, user_id, importer_mapping_id, importer_progress_id, upload_file_name, file_data, modified_data, mapped_data, created_at, updated_at, created_by, updated_by) VALUES ('210c7175-9d7a-454c-b35f-c8aa0ee0a31e', ?::uuid, (select id from bi_user where orcid = '1111-2222-3333-4444'), (select id from importer_mapping where name = 'GermplasmTemplateMap'), 'd02090c3-e40d-45f4-9a63-b8c701c9531b', 'germ import 2.xlsx', '[{"Name": "Gnome-1", "Source": "USDA", "Entry No": "1", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-2", "Source": "USDA", "Entry No": "2", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3", "Source": "Breeding Insight", "Entry No": "3", "External UID": "", "Breeding Method": "BPC", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "2", "Female Parent Entry No": "1"}, {"Name": "Gnome-3-1", "Source": "Breeding Insight", "Entry No": "4", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-2", "Source": "Breeding Insight", "Entry No": "5", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-3", "Source": "Breeding Insight", "Entry No": "6", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-4", "Source": "Breeding Insight", "Entry No": "7", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-5", "Source": "Breeding Insight", "Entry No": "8", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-6", "Source": "Breeding Insight", "Entry No": "9", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-7", "Source": "Breeding Insight", "Entry No": "10", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "11", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "9"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "12", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "3"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "13", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "7"}]', null, '{"rows": [{"germplasm": {"id": "1580b261-f504-4a7e-bbc7-e5599f0b2f4b", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-1 [MNTTIM-1]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "1"}, "commonCropName": "test", "accessionNumber": "1", "defaultDisplayName": "Gnome-1", "externalReferences": [{"referenceID": "6a1269cb-6e7a-4b87-a353-c681c214bd69", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "7e665eb0-2f7b-4b14-85a4-50f5508de2a8", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-2 [MNTTIM-2]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "2"}, "commonCropName": "test", "accessionNumber": "2", "defaultDisplayName": "Gnome-2", "externalReferences": [{"referenceID": "bcc3fab6-04ed-4e2f-b0b0-9e9b247462ef", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "eddb03ff-09c5-481d-9c1c-8fc609d432e5", "state": "NEW", "brAPIObject": {"pedigree": "Gnome-1 [MNTTIM-1]/Gnome-2 [MNTTIM-2]", "seedSource": "Breeding Insight", "germplasmName": "Gnome-3 [MNTTIM-3]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Biparental cross", "breedingMethodId": "aee4d3a3-0df8-422f-b052-d9f0b81e5b4d", "importEntryNumber": "3", "maleParentEntryNo": "2", "femaleParentEntryNo": "1"}, "commonCropName": "test", "accessionNumber": "3", "defaultDisplayName": "Gnome-3", "externalReferences": [{"referenceID": "24c3164e-0b37-44b4-8b18-9118ca9b2949", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "42ff9b3e-3f04-42f3-909b-2aaa158457e5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-1 [MNTTIM-4]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "4"}, "commonCropName": "test", "accessionNumber": "4", "defaultDisplayName": "Gnome-3-1", "externalReferences": [{"referenceID": "c271722b-488b-41c8-9671-73ac008be522", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "883cef8e-34c9-43f7-8062-d7d80c8a65fd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-2 [MNTTIM-5]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "5"}, "commonCropName": "test", "accessionNumber": "5", "defaultDisplayName": "Gnome-3-2", "externalReferences": [{"referenceID": "6bb014ce-0c5f-480c-a1e2-9b87a914a733", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "6215c013-2ed9-43c1-a964-15f87024da36", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-3 [MNTTIM-6]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "6"}, "commonCropName": "test", "accessionNumber": "6", "defaultDisplayName": "Gnome-3-3", "externalReferences": [{"referenceID": "988fe16a-6308-4146-9cae-be9d0cd9c295", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "bd62dec0-25d3-4bef-b9f7-5af634f78366", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-4 [MNTTIM-7]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "7"}, "commonCropName": "test", "accessionNumber": "7", "defaultDisplayName": "Gnome-3-4", "externalReferences": [{"referenceID": "28e6482a-2804-4aa8-b98d-6c4a56f8d8d2", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "c0067078-7a6d-4c99-b6ee-ab220feccddd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-5 [MNTTIM-8]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "8"}, "commonCropName": "test", "accessionNumber": "8", "defaultDisplayName": "Gnome-3-5", "externalReferences": [{"referenceID": "e7c25b55-61f5-4845-9080-d2243171f70b", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "cdb38aac-51fc-4ea7-9b38-8072258ceac5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-6 [MNTTIM-9]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "9"}, "commonCropName": "test", "accessionNumber": "9", "defaultDisplayName": "Gnome-3-6", "externalReferences": [{"referenceID": "ade6b8ca-b2e0-49cc-ac09-09476b1416e7", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "3f6c7d4b-fca2-4b22-aa6a-aadb347cd625", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-7 [MNTTIM-10]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "10"}, "commonCropName": "test", "accessionNumber": "10", "defaultDisplayName": "Gnome-3-7", "externalReferences": [{"referenceID": "6cd34ea3-86b0-41a2-aef0-57c101bd68fb", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "68a72f81-42b5-4425-8034-eef7d6921928", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-6 [MNTTIM-9]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-11]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "11", "maleParentEntryNo": "1", "femaleParentEntryNo": "9"}, "commonCropName": "test", "accessionNumber": "11", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "354faf95-682c-4a62-90f4-4f6c9c2d4191", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "25d84084-45a6-4d93-a683-004f95d3dc3e", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3 [MNTTIM-3]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-12]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "12", "maleParentEntryNo": "1", "femaleParentEntryNo": "3"}, "commonCropName": "test", "accessionNumber": "12", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "936a608f-0e2b-4cee-b348-dbbd57cbdf65", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "569fd3a0-1960-45e0-86d4-5157e21e8130", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-4 [MNTTIM-7]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-13]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "13", "maleParentEntryNo": "1", "femaleParentEntryNo": "7"}, "commonCropName": "test", "accessionNumber": "13", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "4e708554-e088-48ce-a16d-43f1778549fa", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}], "statistics": {"Germplasm": {"newObjectCount": 13}, "Pedigree Connections": {"newObjectCount": 4}}}', '2022-05-18 14:19:33 +00:00', '2022-05-18 14:19:33 +00:00', (select id from bi_user where orcid = '1111-2222-3333-4444'), (select id from bi_user where orcid = '1111-2222-3333-4444')); +INSERT INTO public.importer_import (id, program_id, user_id, importer_mapping_id, importer_progress_id, upload_file_name, file_data, modified_data, mapped_data, created_at, updated_at, created_by, updated_by) VALUES ('210c7175-9d7a-454c-b35f-c8aa0ee0a31e', ?::uuid, (select id from bi_user where oauth_id = '1111-2222-3333-4444'), (select id from importer_mapping where name = 'GermplasmTemplateMap'), 'd02090c3-e40d-45f4-9a63-b8c701c9531b', 'germ import 2.xlsx', '[{"Name": "Gnome-1", "Source": "USDA", "Entry No": "1", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-2", "Source": "USDA", "Entry No": "2", "External UID": "", "Breeding Method": "UMM", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3", "Source": "Breeding Insight", "Entry No": "3", "External UID": "", "Breeding Method": "BPC", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "2", "Female Parent Entry No": "1"}, {"Name": "Gnome-3-1", "Source": "Breeding Insight", "Entry No": "4", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-2", "Source": "Breeding Insight", "Entry No": "5", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-3", "Source": "Breeding Insight", "Entry No": "6", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-4", "Source": "Breeding Insight", "Entry No": "7", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-5", "Source": "Breeding Insight", "Entry No": "8", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-6", "Source": "Breeding Insight", "Entry No": "9", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-3-7", "Source": "Breeding Insight", "Entry No": "10", "External UID": "", "Breeding Method": "CFV", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "", "Female Parent Entry No": ""}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "11", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "9"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "12", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "3"}, {"Name": "Gnome-11", "Source": "Theoretical Breeding Institute", "Entry No": "13", "External UID": "", "Breeding Method": "BCR", "Male Parent GID": "", "Female Parent GID": "", "Male Parent Entry No": "1", "Female Parent Entry No": "7"}]', null, '{"rows": [{"germplasm": {"id": "1580b261-f504-4a7e-bbc7-e5599f0b2f4b", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-1 [MNTTIM-1]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "1"}, "commonCropName": "test", "accessionNumber": "1", "defaultDisplayName": "Gnome-1", "externalReferences": [{"referenceID": "6a1269cb-6e7a-4b87-a353-c681c214bd69", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "7e665eb0-2f7b-4b14-85a4-50f5508de2a8", "state": "NEW", "brAPIObject": {"seedSource": "USDA", "germplasmName": "Gnome-2 [MNTTIM-2]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Unknown maintenance method", "breedingMethodId": "af40db90-64ab-444d-a749-2f857bc90f64", "importEntryNumber": "2"}, "commonCropName": "test", "accessionNumber": "2", "defaultDisplayName": "Gnome-2", "externalReferences": [{"referenceID": "bcc3fab6-04ed-4e2f-b0b0-9e9b247462ef", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "eddb03ff-09c5-481d-9c1c-8fc609d432e5", "state": "NEW", "brAPIObject": {"pedigree": "Gnome-1 [MNTTIM-1]/Gnome-2 [MNTTIM-2]", "seedSource": "Breeding Insight", "germplasmName": "Gnome-3 [MNTTIM-3]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Biparental cross", "breedingMethodId": "aee4d3a3-0df8-422f-b052-d9f0b81e5b4d", "importEntryNumber": "3", "maleParentEntryNo": "2", "femaleParentEntryNo": "1"}, "commonCropName": "test", "accessionNumber": "3", "defaultDisplayName": "Gnome-3", "externalReferences": [{"referenceID": "24c3164e-0b37-44b4-8b18-9118ca9b2949", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "42ff9b3e-3f04-42f3-909b-2aaa158457e5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-1 [MNTTIM-4]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "4"}, "commonCropName": "test", "accessionNumber": "4", "defaultDisplayName": "Gnome-3-1", "externalReferences": [{"referenceID": "c271722b-488b-41c8-9671-73ac008be522", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "883cef8e-34c9-43f7-8062-d7d80c8a65fd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-2 [MNTTIM-5]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "5"}, "commonCropName": "test", "accessionNumber": "5", "defaultDisplayName": "Gnome-3-2", "externalReferences": [{"referenceID": "6bb014ce-0c5f-480c-a1e2-9b87a914a733", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "6215c013-2ed9-43c1-a964-15f87024da36", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-3 [MNTTIM-6]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "6"}, "commonCropName": "test", "accessionNumber": "6", "defaultDisplayName": "Gnome-3-3", "externalReferences": [{"referenceID": "988fe16a-6308-4146-9cae-be9d0cd9c295", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "bd62dec0-25d3-4bef-b9f7-5af634f78366", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-4 [MNTTIM-7]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "7"}, "commonCropName": "test", "accessionNumber": "7", "defaultDisplayName": "Gnome-3-4", "externalReferences": [{"referenceID": "28e6482a-2804-4aa8-b98d-6c4a56f8d8d2", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "c0067078-7a6d-4c99-b6ee-ab220feccddd", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-5 [MNTTIM-8]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "8"}, "commonCropName": "test", "accessionNumber": "8", "defaultDisplayName": "Gnome-3-5", "externalReferences": [{"referenceID": "e7c25b55-61f5-4845-9080-d2243171f70b", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "cdb38aac-51fc-4ea7-9b38-8072258ceac5", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-6 [MNTTIM-9]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "9"}, "commonCropName": "test", "accessionNumber": "9", "defaultDisplayName": "Gnome-3-6", "externalReferences": [{"referenceID": "ade6b8ca-b2e0-49cc-ac09-09476b1416e7", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "3f6c7d4b-fca2-4b22-aa6a-aadb347cd625", "state": "NEW", "brAPIObject": {"seedSource": "Breeding Insight", "germplasmName": "Gnome-3-7 [MNTTIM-10]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Clone formation (Veg)", "breedingMethodId": "48409513-d05d-458a-acf6-a5c501734145", "importEntryNumber": "10"}, "commonCropName": "test", "accessionNumber": "10", "defaultDisplayName": "Gnome-3-7", "externalReferences": [{"referenceID": "6cd34ea3-86b0-41a2-aef0-57c101bd68fb", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "68a72f81-42b5-4425-8034-eef7d6921928", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-6 [MNTTIM-9]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-11]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "11", "maleParentEntryNo": "1", "femaleParentEntryNo": "9"}, "commonCropName": "test", "accessionNumber": "11", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "354faf95-682c-4a62-90f4-4f6c9c2d4191", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "25d84084-45a6-4d93-a683-004f95d3dc3e", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3 [MNTTIM-3]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-12]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "12", "maleParentEntryNo": "1", "femaleParentEntryNo": "3"}, "commonCropName": "test", "accessionNumber": "12", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "936a608f-0e2b-4cee-b348-dbbd57cbdf65", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}, {"germplasm": {"id": "569fd3a0-1960-45e0-86d4-5157e21e8130", "state": "EXISTING", "brAPIObject": {"pedigree": "Gnome-3-4 [MNTTIM-7]/Gnome-1 [MNTTIM-1]", "seedSource": "Theoretical Breeding Institute", "germplasmName": "Gnome-11 [MNTTIM-13]", "additionalInfo": {"createdBy": {"userId": "55e268e9-cc02-44e6-ad1e-d99ea9770830", "userName": "BI-DEV Admin"}, "createdDate": "17/05/2022 10:19:54", "breedingMethod": "Backcross", "breedingMethodId": "2ac64ec4-19af-4b4a-946f-9b1be0cc1730", "importEntryNumber": "13", "maleParentEntryNo": "1", "femaleParentEntryNo": "7"}, "commonCropName": "test", "accessionNumber": "13", "defaultDisplayName": "Gnome-11", "externalReferences": [{"referenceID": "4e708554-e088-48ce-a16d-43f1778549fa", "referenceSource": "breedinginsight.org"}, {"referenceID": "ee801336-c885-44f1-873f-666ec6e0740a", "referenceSource": "breedinginsight.org/programs"}]}}}], "statistics": {"Germplasm": {"newObjectCount": 13}, "Pedigree Connections": {"newObjectCount": 4}}}', '2022-05-18 14:19:33 +00:00', '2022-05-18 14:19:33 +00:00', (select id from bi_user where oauth_id = '1111-2222-3333-4444'), (select id from bi_user where oauth_id = '1111-2222-3333-4444')); From e9386dd1a816c975eee9988d0e0cff796d4a73e7 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 1 Jul 2025 10:32:46 -0400 Subject: [PATCH 06/14] [BI-2540] - bumped migration version to avoid conflict --- .../{V1.33.0__rename-orcid.sql => V1.34.0__rename-orcid.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V1.33.0__rename-orcid.sql => V1.34.0__rename-orcid.sql} (100%) diff --git a/src/main/resources/db/migration/V1.33.0__rename-orcid.sql b/src/main/resources/db/migration/V1.34.0__rename-orcid.sql similarity index 100% rename from src/main/resources/db/migration/V1.33.0__rename-orcid.sql rename to src/main/resources/db/migration/V1.34.0__rename-orcid.sql From a8bd40d1f0ec0541fdf5da3e58a730c2d8ba2fa4 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 1 Jul 2025 14:39:19 -0400 Subject: [PATCH 07/14] [BI-2540] - added GitHub OAuth variables to docker-compose.yml --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index efebf5fb6..f45227ea7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,8 @@ services: - API_INTERNAL_PORT=${API_INTERNAL_PORT} - API_INTERNAL_TEST_PORT=${API_INTERNAL_TEST_PORT} - OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID} + - GITHUB_OAUTH_CLIENT_ID=${GITHUB_OAUTH_CLIENT_ID} + - GITHUB_OAUTH_CLIENT_SECRET=${GITHUB_OAUTH_CLIENT_SECRET} - JWT_DOMAIN=${JWT_DOMAIN} - DB_SERVER=${DB_SERVER} - DB_NAME=${DB_NAME} From db262d3eb30d0600279aff11c6fde742ccd190b4 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 1 Jul 2025 14:42:18 -0400 Subject: [PATCH 08/14] [BI-2540] - added (placeholder) GitHub OAuth variables to build.yml --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f77a41e0..0e1b29b66 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,4 +59,6 @@ jobs: JWT_SECRET: ${{ secrets.JWT_SECRET }} OAUTH_CLIENT_ID: 123abc OAUTH_CLIENT_SECRET: asdfljkhalkbaldsfjasdfi238497098asdf + GITHUB_OAUTH_CLIENT_ID: 12345678901234567890 + GITHUB_OAUTH_CLIENT_SECRET: 1234567890123456789012345678901234567890 BRAPI_REFERENCE_SOURCE: breedinginsight.org \ No newline at end of file From 995d57133690172fbf1a36f5ab7523e2489c1442 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Tue, 1 Jul 2025 16:00:49 -0400 Subject: [PATCH 09/14] [BI-2540] - renamed constraint --- src/main/resources/db/migration/V1.34.0__rename-orcid.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/db/migration/V1.34.0__rename-orcid.sql b/src/main/resources/db/migration/V1.34.0__rename-orcid.sql index 928a426fa..5c559237c 100644 --- a/src/main/resources/db/migration/V1.34.0__rename-orcid.sql +++ b/src/main/resources/db/migration/V1.34.0__rename-orcid.sql @@ -19,6 +19,10 @@ ALTER TABLE bi_user RENAME COLUMN orcid TO oauth_id; +-- Rename unique constraint. +ALTER TABLE bi_user +RENAME CONSTRAINT orcid_unique TO oauth_id_unique; + -- Add a column to store the OAuth provider with 'orcid' as the default value. ALTER TABLE bi_user ADD COLUMN oauth_provider text DEFAULT 'orcid'; From 0cf53b8f2ef1e61df7bb5fa522740e04b2a89719 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 10 Jul 2025 14:45:09 -0400 Subject: [PATCH 10/14] [BI-2540] - added migration locations for flyway maven plugin --- .env.template | 5 ++++- pom.xml | 1 + src/main/resources/application.yml | 6 ++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.env.template b/.env.template index 40910d2dd..bedcdfd54 100644 --- a/.env.template +++ b/.env.template @@ -77,4 +77,7 @@ STUDY_START_DELAY=10s TRIAL_START_DELAY=15s TRAIT_START_DELAY=20s OBSERVATION_START_DELAY=25s -OBSERVATION_UNIT_START_DELAY=30s \ No newline at end of file +OBSERVATION_UNIT_START_DELAY=30s + +# Migration locations for both maven flyway plugin (configured in pom.xml) and micronaut flyway library (configured in application.yml). +FLYWAY_LOCATIONS=classpath:org/breedinginsight/db/migration,classpath:db/migration \ No newline at end of file diff --git a/pom.xml b/pom.xml index 52567cddb..edf2b0eb9 100644 --- a/pom.xml +++ b/pom.xml @@ -624,6 +624,7 @@ jdbc:postgresql://${DB_SERVER}/${DB_NAME} ${DB_USER} ${DB_PASSWORD} + ${FLYWAY_LOCATIONS} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 616747556..2aa779ac6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -114,15 +114,13 @@ jackson: flyway: datasources: default: - locations: - - classpath:org/breedinginsight/db/migration - - classpath:db/migration + locations: ${FLYWAY_LOCATIONS:classpath:org/breedinginsight/db/migration,classpath:db/migration} enabled: true placeholders: default-url: ${brapi.server.default-url} brapi-reference-source: ${brapi.server.reference-source} orcid-sandbox-authentication: ${ORCID_SANDBOX_AUTHENTICATION:false} - out-of-order: false + out-of-order: true jooq: datasources: default: From 509fcd9bea31494aee6005e4e6d751f0a5f10a90 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Thu, 10 Jul 2025 15:01:51 -0400 Subject: [PATCH 11/14] [BI-2540] - added configuration to workflow files --- .github/workflows/build.yml | 3 ++- .github/workflows/release.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0e1b29b66..73693e353 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,4 +61,5 @@ jobs: OAUTH_CLIENT_SECRET: asdfljkhalkbaldsfjasdfi238497098asdf GITHUB_OAUTH_CLIENT_ID: 12345678901234567890 GITHUB_OAUTH_CLIENT_SECRET: 1234567890123456789012345678901234567890 - BRAPI_REFERENCE_SOURCE: breedinginsight.org \ No newline at end of file + BRAPI_REFERENCE_SOURCE: breedinginsight.org + FLYWAY_LOCATIONS: classpath:org/breedinginsight/db/migration,classpath:db/migration \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 31d1bd0e6..20f060783 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,6 +67,7 @@ jobs: OAUTH_CLIENT_ID: 123abc OAUTH_CLIENT_SECRET: asdfljkhalkbaldsfjasdfi238497098asdf BRAPI_REFERENCE_SOURCE: breedinginsight.org + FLYWAY_LOCATIONS: classpath:org/breedinginsight/db/migration,classpath:db/migration - name: Login to Docker Hub uses: docker/login-action@v1 with: From c2eb985818e307411b9bfb33729a246bfa44b9a5 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:56:14 -0400 Subject: [PATCH 12/14] [BI-2540] - updated flyway locations paths for CI --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 73693e353..db3a9a148 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,4 +62,4 @@ jobs: GITHUB_OAUTH_CLIENT_ID: 12345678901234567890 GITHUB_OAUTH_CLIENT_SECRET: 1234567890123456789012345678901234567890 BRAPI_REFERENCE_SOURCE: breedinginsight.org - FLYWAY_LOCATIONS: classpath:org/breedinginsight/db/migration,classpath:db/migration \ No newline at end of file + FLYWAY_LOCATIONS: filesystem:src/main/java/org/breedinginsight/db/migration,filesystem:src/main/resources/db/migration \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20f060783..4a5be2c73 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: OAUTH_CLIENT_ID: 123abc OAUTH_CLIENT_SECRET: asdfljkhalkbaldsfjasdfi238497098asdf BRAPI_REFERENCE_SOURCE: breedinginsight.org - FLYWAY_LOCATIONS: classpath:org/breedinginsight/db/migration,classpath:db/migration + FLYWAY_LOCATIONS: filesystem:src/main/java/org/breedinginsight/db/migration,filesystem:src/main/resources/db/migration - name: Login to Docker Hub uses: docker/login-action@v1 with: From a8a0eb29c33f5bcb09444e3752bce721c8f4d5f6 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 30 Jul 2025 10:18:33 -0400 Subject: [PATCH 13/14] [BI-2540] - updated comment --- .env.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.template b/.env.template index 75be429a5..711c01221 100644 --- a/.env.template +++ b/.env.template @@ -3,7 +3,7 @@ USER_ID= GROUP_ID= -# GitHub OAuth variables. Only required if using GitHub as an alternative to ORCID. +# GitHub OAuth variables. Only required if using GitHub as an OAuth provider. GITHUB_OAUTH_CLIENT_ID= GITHUB_OAUTH_CLIENT_SECRET= From 73aa94871ce06fd828f32f364ee2bf6c165f6620 Mon Sep 17 00:00:00 2001 From: mlm483 <128052931+mlm483@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:59:57 -0400 Subject: [PATCH 14/14] [BI-2540] - fixed GitHub Actions --- .env.template | 3 - .github/workflows/build.yml | 1 - .github/workflows/gemini-cli.yml | 304 ++++++++++++ .../gemini-issue-automated-triage.yml | 130 +++++ .../gemini-issue-scheduled-triage.yml | 123 +++++ .github/workflows/gemini-pr-review.yml | 456 ++++++++++++++++++ .github/workflows/release.yml | 1 - pom.xml | 5 +- .../brapi/v2/services/BrAPITrialService.java | 16 +- .../steps/CommitPendingImportObjectsStep.java | 61 +-- src/main/resources/application.yml | 4 +- src/main/resources/version.properties | 4 +- 12 files changed, 1065 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/gemini-cli.yml create mode 100644 .github/workflows/gemini-issue-automated-triage.yml create mode 100644 .github/workflows/gemini-issue-scheduled-triage.yml create mode 100644 .github/workflows/gemini-pr-review.yml diff --git a/.env.template b/.env.template index 711c01221..0e768adcd 100644 --- a/.env.template +++ b/.env.template @@ -79,6 +79,3 @@ TRIAL_START_DELAY=15s TRAIT_START_DELAY=20s OBSERVATION_START_DELAY=25s OBSERVATION_UNIT_START_DELAY=30s - -# Migration locations for both maven flyway plugin (configured in pom.xml) and micronaut flyway library (configured in application.yml). -FLYWAY_LOCATIONS=classpath:org/breedinginsight/db/migration,classpath:db/migration \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c220ab81b..1505d8111 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,5 +76,4 @@ jobs: GITHUB_OAUTH_CLIENT_ID: 12345678901234567890 GITHUB_OAUTH_CLIENT_SECRET: 1234567890123456789012345678901234567890 BRAPI_REFERENCE_SOURCE: breedinginsight.org - FLYWAY_LOCATIONS: filesystem:src/main/java/org/breedinginsight/db/migration,filesystem:src/main/resources/db/migration BRAPI_DOCKER_IMAGE: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.brapi_server_image || 'breedinginsight/brapi-java-server:develop' }} diff --git a/.github/workflows/gemini-cli.yml b/.github/workflows/gemini-cli.yml new file mode 100644 index 000000000..3fbaedc70 --- /dev/null +++ b/.github/workflows/gemini-cli.yml @@ -0,0 +1,304 @@ +name: '💬 Gemini CLI' + +on: + pull_request_review_comment: + types: + - 'created' + pull_request_review: + types: + - 'submitted' + issue_comment: + types: + - 'created' + +concurrency: + group: '${{ github.workflow }}-${{ github.event.issue.number }}' + cancel-in-progress: |- + ${{ github.event.sender.type == 'User' && ( github.event.issue.author_association == 'OWNER' || github.event.issue.author_association == 'MEMBER' || github.event.issue.author_association == 'COLLABORATOR') }} + +defaults: + run: + shell: 'bash' + +permissions: + contents: 'write' + id-token: 'write' + pull-requests: 'write' + issues: 'write' + +jobs: + gemini-cli: + # This condition is complex to ensure we only run when explicitly invoked. + if: |- + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'issues' && github.event.action == 'opened' && + contains(github.event.issue.body, '@gemini-cli') && + !contains(github.event.issue.body, '@gemini-cli /review') && + !contains(github.event.issue.body, '@gemini-cli /triage') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association) + ) || + ( + ( + github.event_name == 'issue_comment' || + github.event_name == 'pull_request_review_comment' + ) && + contains(github.event.comment.body, '@gemini-cli') && + !contains(github.event.comment.body, '@gemini-cli /review') && + !contains(github.event.comment.body, '@gemini-cli /triage') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ) || + ( + github.event_name == 'pull_request_review' && + contains(github.event.review.body, '@gemini-cli') && + !contains(github.event.review.body, '@gemini-cli /review') && + !contains(github.event.review.body, '@gemini-cli /triage') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ) + timeout-minutes: 10 + runs-on: 'ubuntu-latest' + + steps: + - name: 'Generate GitHub App Token' + id: 'generate_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + + - name: 'Get context from event' + id: 'get_context' + env: + EVENT_NAME: '${{ github.event_name }}' + EVENT_PAYLOAD: '${{ toJSON(github.event) }}' + run: |- + set -euo pipefail + + USER_REQUEST="" + ISSUE_NUMBER="" + IS_PR="false" + + if [[ "${EVENT_NAME}" == "issues" ]]; then + USER_REQUEST=$(echo "${EVENT_PAYLOAD}" | jq -r .issue.body) + ISSUE_NUMBER=$(echo "${EVENT_PAYLOAD}" | jq -r .issue.number) + elif [[ "${EVENT_NAME}" == "issue_comment" ]]; then + USER_REQUEST=$(echo "${EVENT_PAYLOAD}" | jq -r .comment.body) + ISSUE_NUMBER=$(echo "${EVENT_PAYLOAD}" | jq -r .issue.number) + if [[ $(echo "${EVENT_PAYLOAD}" | jq -r .issue.pull_request) != "null" ]]; then + IS_PR="true" + fi + elif [[ "${EVENT_NAME}" == "pull_request_review" ]]; then + USER_REQUEST=$(echo "${EVENT_PAYLOAD}" | jq -r .review.body) + ISSUE_NUMBER=$(echo "${EVENT_PAYLOAD}" | jq -r .pull_request.number) + IS_PR="true" + elif [[ "${EVENT_NAME}" == "pull_request_review_comment" ]]; then + USER_REQUEST=$(echo "${EVENT_PAYLOAD}" | jq -r .comment.body) + ISSUE_NUMBER=$(echo "${EVENT_PAYLOAD}" | jq -r .pull_request.number) + IS_PR="true" + fi + + # Clean up user request + USER_REQUEST=$(echo "${USER_REQUEST}" | sed 's/.*@gemini-cli//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + + { + echo "user_request=${USER_REQUEST}" + echo "issue_number=${ISSUE_NUMBER}" + echo "is_pr=${IS_PR}" + } >> "${GITHUB_OUTPUT}" + + - name: 'Set up git user for commits' + run: |- + git config --global user.name 'gemini-cli[bot]' + git config --global user.email 'gemini-cli[bot]@users.noreply.github.com' + + - name: 'Checkout PR branch' + if: |- + ${{ steps.get_context.outputs.is_pr == 'true' }} + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 + with: + token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + repository: '${{ github.repository }}' + ref: 'refs/pull/${{ steps.get_context.outputs.issue_number }}/head' + fetch-depth: 0 + + - name: 'Checkout main branch' + if: |- + ${{ steps.get_context.outputs.is_pr == 'false' }} + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 + with: + token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + repository: '${{ github.repository }}' + fetch-depth: 0 + + - name: 'Acknowledge request' + env: + GITHUB_ACTOR: '${{ github.actor }}' + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' + REPOSITORY: '${{ github.repository }}' + REQUEST_TYPE: '${{ steps.get_context.outputs.request_type }}' + run: |- + set -euo pipefail + MESSAGE="@${GITHUB_ACTOR} I've received your request and I'm working on it now! 🤖" + if [[ -n "${MESSAGE}" ]]; then + gh issue comment "${ISSUE_NUMBER}" \ + --body "${MESSAGE}" \ + --repo "${REPOSITORY}" + fi + + - name: 'Get description' + id: 'get_description' + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + IS_PR: '${{ steps.get_context.outputs.is_pr }}' + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' + run: |- + set -euo pipefail + if [[ "${IS_PR}" == "true" ]]; then + DESCRIPTION=$(gh pr view "${ISSUE_NUMBER}" --json body --template '{{.body}}') + else + DESCRIPTION=$(gh issue view "${ISSUE_NUMBER}" --json body --template '{{.body}}') + fi + { + echo "description<> "${GITHUB_OUTPUT}" + + - name: 'Get comments' + id: 'get_comments' + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + IS_PR: '${{ steps.get_context.outputs.is_pr }}' + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' + run: |- + set -euo pipefail + if [[ "${IS_PR}" == "true" ]]; then + COMMENTS=$(gh pr view "${ISSUE_NUMBER}" --json comments --template '{{range .comments}}{{.author.login}}: {{.body}}{{"\n"}}{{end}}') + else + COMMENTS=$(gh issue view "${ISSUE_NUMBER}" --json comments --template '{{range .comments}}{{.author.login}}: {{.body}}{{"\n"}}{{end}}') + fi + { + echo "comments<> "${GITHUB_OUTPUT}" + + - name: 'Run Gemini' + id: 'run_gemini' + uses: 'google-github-actions/run-gemini-cli@v0' + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + REPOSITORY: '${{ github.repository }}' + USER_REQUEST: '${{ steps.get_context.outputs.user_request }}' + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' + IS_PR: '${{ steps.get_context.outputs.is_pr }}' + with: + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + settings: |- + { + "maxSessionTurns": 50, + "telemetry": { + "enabled": false, + "target": "gcp" + } + } + prompt: |- + ## Role + + You are a helpful AI assistant invoked via a CLI interface in a GitHub workflow. You have access to tools to interact with the repository and respond to the user. + + ## Context + + - **Repository**: `${{ github.repository }}` + - **Triggering Event**: `${{ github.event_name }}` + - **Issue/PR Number**: `${{ steps.get_context.outputs.issue_number }}` + - **Is this a PR?**: `${{ steps.get_context.outputs.is_pr }}` + - **Issue/PR Description**: + `${{ steps.get_description.outputs.description }}` + - **Comments**: + `${{ steps.get_comments.outputs.comments }}` + + ## User Request + + The user has sent the following request: + `${{ steps.get_context.outputs.user_request }}` + + ## How to Respond to Issues, PR Comments, and Questions + + This workflow supports three main scenarios: + + 1. **Creating a Fix for an Issue** + - Carefully read the user request and the related issue or PR description. + - Use available tools to gather all relevant context (e.g., `gh issue view`, `gh pr view`, `gh pr diff`, `cat`, `head`, `tail`). + - Identify the root cause of the problem before proceeding. + - **Show and maintain a plan as a checklist**: + - At the very beginning, outline the steps needed to resolve the issue or address the request and post them as a checklist comment on the issue or PR (use GitHub markdown checkboxes: `- [ ] Task`). + - Example: + ``` + ### Plan + - [ ] Investigate the root cause + - [ ] Implement the fix in `file.py` + - [ ] Add/modify tests + - [ ] Update documentation + - [ ] Verify the fix and close the issue + ``` + - Use: `gh pr comment "${ISSUE_NUMBER}" --body ""` or `gh issue comment "${ISSUE_NUMBER}" --body ""` to post the initial plan. + - As you make progress, keep the checklist visible and up to date by editing the same comment (check off completed tasks with `- [x]`). + - To update the checklist: + 1. Find the comment ID for the checklist (use `gh pr comment list "${ISSUE_NUMBER}"` or `gh issue comment list "${ISSUE_NUMBER}"`). + 2. Edit the comment with the updated checklist: + - For PRs: `gh pr comment --edit --body ""` + - For Issues: `gh issue comment --edit --body ""` + 3. The checklist should only be maintained as a comment on the issue or PR. Do not track or update the checklist in code files. + - If the fix requires code changes, determine which files and lines are affected. If clarification is needed, note any questions for the user. + - Make the necessary code or documentation changes using the available tools (e.g., `write_file`). Ensure all changes follow project conventions and best practices. Reference all shell variables as `"${VAR}"` (with quotes and braces) to prevent errors. + - Run any relevant tests or checks to verify the fix works as intended. If possible, provide evidence (test output, screenshots, etc.) that the issue is resolved. + - **Branching and Committing**: + - **NEVER commit directly to the `main` branch.** + - If you are working on a **pull request** (`IS_PR` is `true`), the correct branch is already checked out. Simply commit and push to it. + - `git add .` + - `git commit -m "feat: "` + - `git push` + - If you are working on an **issue** (`IS_PR` is `false`), create a new branch for your changes. A good branch name would be `issue/${ISSUE_NUMBER}/`. + - `git checkout -b issue/${ISSUE_NUMBER}/my-fix` + - `git add .` + - `git commit -m "feat: "` + - `git push origin issue/${ISSUE_NUMBER}/my-fix` + - After pushing, you can create a pull request: `gh pr create --title "Fixes #${ISSUE_NUMBER}: " --body "This PR addresses issue #${ISSUE_NUMBER}."` + - Summarize what was changed and why in a markdown file: `write_file("response.md", "")` + - Post the response as a comment: + - For PRs: `gh pr comment "${ISSUE_NUMBER}" --body-file response.md` + - For Issues: `gh issue comment "${ISSUE_NUMBER}" --body-file response.md` + + 2. **Addressing Comments on a Pull Request** + - Read the specific comment and the context of the PR. + - Use tools like `gh pr view`, `gh pr diff`, and `cat` to understand the code and discussion. + - If the comment requests a change or clarification, follow the same process as for fixing an issue: create a checklist plan, implement, test, and commit any required changes, updating the checklist as you go. + - **Committing Changes**: The correct PR branch is already checked out. Simply add, commit, and push your changes. + - `git add .` + - `git commit -m "fix: address review comments"` + - `git push` + - If the comment is a question, answer it directly and clearly, referencing code or documentation as needed. + - Document your response in `response.md` and post it as a PR comment: `gh pr comment "${ISSUE_NUMBER}" --body-file response.md` + + 3. **Answering Any Question on an Issue** + - Read the question and the full issue context using `gh issue view` and related tools. + - Research or analyze the codebase as needed to provide an accurate answer. + - If the question requires code or documentation changes, follow the fix process above, including creating and updating a checklist plan and **creating a new branch for your changes as described in section 1.** + - Write a clear, concise answer in `response.md` and post it as an issue comment: `gh issue comment "${ISSUE_NUMBER}" --body-file response.md` + + ## Guidelines + + - **Be concise and actionable.** Focus on solving the user's problem efficiently. + - **Always commit and push your changes if you modify code or documentation.** + - **If you are unsure about the fix or answer, explain your reasoning and ask clarifying questions.** + - **Follow project conventions and best practices.** diff --git a/.github/workflows/gemini-issue-automated-triage.yml b/.github/workflows/gemini-issue-automated-triage.yml new file mode 100644 index 000000000..50a671018 --- /dev/null +++ b/.github/workflows/gemini-issue-automated-triage.yml @@ -0,0 +1,130 @@ +name: '🏷️ Gemini Automated Issue Triage' + +on: + issues: + types: + - 'opened' + - 'reopened' + issue_comment: + types: + - 'created' + workflow_dispatch: + inputs: + issue_number: + description: 'issue number to triage' + required: true + type: 'number' + +concurrency: + group: '${{ github.workflow }}-${{ github.event.issue.number }}' + cancel-in-progress: true + +defaults: + run: + shell: 'bash' + +permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + statuses: 'write' + +jobs: + triage-issue: + if: |- + github.event_name == 'issues' || + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'issue_comment' && + contains(github.event.comment.body, '@gemini-cli /triage') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ) + timeout-minutes: 5 + runs-on: 'ubuntu-latest' + + steps: + - name: 'Checkout repository' + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 + + - name: 'Generate GitHub App Token' + id: 'generate_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + + - name: 'Run Gemini Issue Triage' + uses: 'google-github-actions/run-gemini-cli@v0' + id: 'gemini_issue_triage' + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + ISSUE_TITLE: '${{ github.event.issue.title }}' + ISSUE_BODY: '${{ github.event.issue.body }}' + ISSUE_NUMBER: '${{ github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + with: + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + settings: |- + { + "maxSessionTurns": 25, + "coreTools": [ + "run_shell_command(echo)", + "run_shell_command(gh label list)", + "run_shell_command(gh issue edit)" + ], + "telemetry": { + "enabled": false, + "target": "gcp" + } + } + prompt: |- + ## Role + + You are an issue triage assistant. Analyze the current GitHub issue + and apply the most appropriate existing labels. Use the available + tools to gather information; do not ask for information to be + provided. + + ## Steps + + 1. Run: `gh label list` to get all available labels. + 2. Review the issue title and body provided in the environment + variables: "${ISSUE_TITLE}" and "${ISSUE_BODY}". + 3. Classify issues by their kind (bug, enhancement, documentation, + cleanup, etc) and their priority (p0, p1, p2, p3). Set the + labels accoridng to the format `kind/*` and `priority/*` patterns. + 4. Apply the selected labels to this issue using: + `gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"` + 5. If the "status/needs-triage" label is present, remove it using: + `gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"` + + ## Guidelines + + - Only use labels that already exist in the repository + - Do not add comments or modify the issue content + - Triage only the current issue + - Assign all applicable labels based on the issue content + - Reference all shell variables as "${VAR}" (with quotes and braces) + + - name: 'Post Issue Triage Failure Comment' + if: |- + ${{ failure() && steps.gemini_issue_triage.outcome == 'failure' }} + uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' + with: + github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + script: |- + github.rest.issues.createComment({ + owner: '${{ github.repository }}'.split('/')[0], + repo: '${{ github.repository }}'.split('/')[1], + issue_number: '${{ github.event.issue.number }}', + body: 'There is a problem with the Gemini CLI issue triaging. Please check the [action logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.' + }) diff --git a/.github/workflows/gemini-issue-scheduled-triage.yml b/.github/workflows/gemini-issue-scheduled-triage.yml new file mode 100644 index 000000000..f44205973 --- /dev/null +++ b/.github/workflows/gemini-issue-scheduled-triage.yml @@ -0,0 +1,123 @@ +name: '📋 Gemini Scheduled Issue Triage' + +on: + schedule: + - cron: '0 * * * *' # Runs every hour + workflow_dispatch: + +concurrency: + group: '${{ github.workflow }}' + cancel-in-progress: true + +defaults: + run: + shell: 'bash' + +permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + statuses: 'write' + +jobs: + triage-issues: + timeout-minutes: 5 + runs-on: 'ubuntu-latest' + + steps: + - name: 'Checkout repository' + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 + + - name: 'Generate GitHub App Token' + id: 'generate_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + + - name: 'Find untriaged issues' + id: 'find_issues' + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + GITHUB_REPOSITORY: '${{ github.repository }}' + GITHUB_OUTPUT: '${{ github.output }}' + run: |- + set -euo pipefail + + echo '🔍 Finding issues without labels...' + NO_LABEL_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \ + --search 'is:open is:issue no:label' --json number,title,body)" + + echo '🏷️ Finding issues that need triage...' + NEED_TRIAGE_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \ + --search 'is:open is:issue label:"status/needs-triage"' --json number,title,body)" + + echo '🔄 Merging and deduplicating issues...' + ISSUES="$(echo "${NO_LABEL_ISSUES}" "${NEED_TRIAGE_ISSUES}" | jq -c -s 'add | unique_by(.number)')" + + echo '📝 Setting output for GitHub Actions...' + echo "issues_to_triage=${ISSUES}" >> "${GITHUB_OUTPUT}" + + ISSUE_COUNT="$(echo "${ISSUES}" | jq 'length')" + echo "✅ Found ${ISSUE_COUNT} issues to triage! 🎯" + + - name: 'Run Gemini Issue Triage' + if: |- + ${{ steps.find_issues.outputs.issues_to_triage != '[]' }} + uses: 'google-github-actions/run-gemini-cli@v0' + id: 'gemini_issue_triage' + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + ISSUES_TO_TRIAGE: '${{ steps.find_issues.outputs.issues_to_triage }}' + REPOSITORY: '${{ github.repository }}' + with: + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + settings: |- + { + "maxSessionTurns": 25, + "coreTools": [ + "run_shell_command(echo)", + "run_shell_command(gh label list)", + "run_shell_command(gh issue edit)", + "run_shell_command(gh issue list)" + ], + "telemetry": { + "enabled": false, + "target": "gcp" + } + } + prompt: |- + ## Role + + You are an issue triage assistant. Analyze issues and apply + appropriate labels. Use the available tools to gather information; + do not ask for information to be provided. + + ## Steps + + 1. Run: `gh label list` + 2. Check environment variable: "${ISSUES_TO_TRIAGE}" (JSON array + of issues) + 3. For each issue, apply labels: + `gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"`. + If available, set labels that follow the `kind/*`, `area/*`, + and `priority/*` patterns. + 4. For each issue, if the `status/needs-triage` label is present, + remove it using: + `gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"` + + ## Guidelines + + - Only use existing repository labels + - Do not add comments + - Triage each issue independently + - Reference all shell variables as "${VAR}" (with quotes and braces) diff --git a/.github/workflows/gemini-pr-review.yml b/.github/workflows/gemini-pr-review.yml new file mode 100644 index 000000000..297c4572d --- /dev/null +++ b/.github/workflows/gemini-pr-review.yml @@ -0,0 +1,456 @@ +name: '🧐 Gemini Pull Request Review' + +on: + pull_request: + types: + - 'opened' + - 'reopened' + issue_comment: + types: + - 'created' + pull_request_review_comment: + types: + - 'created' + pull_request_review: + types: + - 'submitted' + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to review' + required: true + type: 'number' + +concurrency: + group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}' + cancel-in-progress: true + +defaults: + run: + shell: 'bash' + +permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + statuses: 'write' + +jobs: + review-pr: + if: |- + github.event_name == 'workflow_dispatch' || + ( + github.event_name == 'pull_request' && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) + ) || + ( + ( + ( + github.event_name == 'issue_comment' && + github.event.issue.pull_request + ) || + github.event_name == 'pull_request_review_comment' + ) && + contains(github.event.comment.body, '@gemini-cli /review') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ) || + ( + github.event_name == 'pull_request_review' && + contains(github.event.review.body, '@gemini-cli /review') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ) + timeout-minutes: 5 + runs-on: 'ubuntu-latest' + + steps: + - name: 'Checkout PR code' + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 + + - name: 'Generate GitHub App Token' + id: 'generate_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + + - name: 'Get PR details (pull_request & workflow_dispatch)' + id: 'get_pr' + if: |- + ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }} + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + EVENT_NAME: '${{ github.event_name }}' + WORKFLOW_PR_NUMBER: '${{ github.event.inputs.pr_number }}' + PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number }}' + run: |- + set -euo pipefail + + if [[ "${EVENT_NAME}" = "workflow_dispatch" ]]; then + PR_NUMBER="${WORKFLOW_PR_NUMBER}" + else + PR_NUMBER="${PULL_REQUEST_NUMBER}" + fi + + echo "pr_number=${PR_NUMBER}" >> "${GITHUB_OUTPUT}" + + # Get PR details + PR_DATA="$(gh pr view "${PR_NUMBER}" --json title,body,additions,deletions,changedFiles,baseRefName,headRefName)" + echo "pr_data=${PR_DATA}" >> "${GITHUB_OUTPUT}" + + # Get file changes + CHANGED_FILES="$(gh pr diff "${PR_NUMBER}" --name-only)" + { + echo "changed_files<> "${GITHUB_OUTPUT}" + + + - name: 'Get PR details (issue_comment)' + id: 'get_pr_comment' + if: |- + ${{ github.event_name == 'issue_comment' }} + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + COMMENT_BODY: '${{ github.event.comment.body }}' + PR_NUMBER: '${{ github.event.issue.number }}' + run: |- + set -euo pipefail + + echo "pr_number=${PR_NUMBER}" >> "${GITHUB_OUTPUT}" + + # Extract additional instructions from comment + ADDITIONAL_INSTRUCTIONS="$( + echo "${COMMENT_BODY}" | sed 's/.*@gemini-cli \/review//' | xargs + )" + echo "additional_instructions=${ADDITIONAL_INSTRUCTIONS}" >> "${GITHUB_OUTPUT}" + + # Get PR details + PR_DATA="$(gh pr view "${PR_NUMBER}" --json title,body,additions,deletions,changedFiles,baseRefName,headRefName)" + echo "pr_data=${PR_DATA}" >> "${GITHUB_OUTPUT}" + + # Get file changes + CHANGED_FILES="$(gh pr diff "${PR_NUMBER}" --name-only)" + { + echo "changed_files<> "${GITHUB_OUTPUT}" + + - name: 'Run Gemini PR Review' + uses: 'google-github-actions/run-gemini-cli@v0' + id: 'gemini_pr_review' + env: + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + PR_NUMBER: '${{ steps.get_pr.outputs.pr_number || steps.get_pr_comment.outputs.pr_number }}' + PR_DATA: '${{ steps.get_pr.outputs.pr_data || steps.get_pr_comment.outputs.pr_data }}' + CHANGED_FILES: '${{ steps.get_pr.outputs.changed_files || steps.get_pr_comment.outputs.changed_files }}' + ADDITIONAL_INSTRUCTIONS: '${{ steps.get_pr.outputs.additional_instructions || steps.get_pr_comment.outputs.additional_instructions }}' + REPOSITORY: '${{ github.repository }}' + with: + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + settings: |- + { + "maxSessionTurns": 20, + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "includeTools": [ + "create_pending_pull_request_review", + "add_comment_to_pending_review", + "submit_pending_pull_request_review" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + } + } + }, + "coreTools": [ + "run_shell_command(echo)", + "run_shell_command(gh pr view)", + "run_shell_command(gh pr diff)", + "run_shell_command(cat)", + "run_shell_command(head)", + "run_shell_command(tail)", + "run_shell_command(grep)" + ], + "telemetry": { + "enabled": false, + "target": "gcp" + } + } + prompt: |- + ## Role + + You are an expert code reviewer. You have access to tools to gather + PR information and perform the review on GitHub. Use the available tools to + gather information; do not ask for information to be provided. + + ## Requirements + 1. All feedback must be left on GitHub. + 2. Any output that is not left in GitHub will not be seen. + + ## Steps + + Start by running these commands to gather the required data: + 1. Run: echo "${REPOSITORY}" to get the github repository in / format + 2. Run: echo "${PR_DATA}" to get PR details (JSON format) + 3. Run: echo "${CHANGED_FILES}" to get the list of changed files + 4. Run: echo "${PR_NUMBER}" to get the PR number + 5. Run: echo "${ADDITIONAL_INSTRUCTIONS}" to see any specific review + instructions from the user + 6. Run: gh pr diff "${PR_NUMBER}" to see the full diff and reference + Context section to understand it + 7. For any specific files, use: cat filename, head -50 filename, or + tail -50 filename + 8. If ADDITIONAL_INSTRUCTIONS contains text, prioritize those + specific areas or focus points in your review. Common instruction + examples: "focus on security", "check performance", "review error + handling", "check for breaking changes" + + ## Guideline + ### Core Guideline(Always applicable) + + 1. Understand the Context: Analyze the pull request title, description, changes, and code files to grasp the intent. + 2. Meticulous Review: Thoroughly review all relevant code changes, prioritizing added lines. Consider the specified + focus areas and any provided style guide. + 3. Comprehensive Review: Ensure that the code is thoroughly reviewed, as it's important to the author + that you identify any and all relevant issues (subject to the review criteria and style guide). + Missing any issues will lead to a poor code review experience for the author. + 4. Constructive Feedback: + * Provide clear explanations for each concern. + * Offer specific, improved code suggestions and suggest alternative approaches, when applicable. + Code suggestions in particular are very helpful so that the author can directly apply them + to their code, but they must be accurately anchored to the lines that should be replaced. + 5. Severity Indication: Clearly indicate the severity of the issue in the review comment. + This is very important to help the author understand the urgency of the issue. + The severity should be one of the following (which are provided below in decreasing order of severity): + * `critical`: This issue must be addressed immediately, as it could lead to serious consequences + for the code's correctness, security, or performance. + * `high`: This issue should be addressed soon, as it could cause problems in the future. + * `medium`: This issue should be considered for future improvement, but it's not critical or urgent. + * `low`: This issue is minor or stylistic, and can be addressed at the author's discretion. + 6. Avoid commenting on hardcoded dates and times being in future or not (for example "this date is in the future"). + * Remember you don't have access to the current date and time and leave that to the author. + 7. Targeted Suggestions: Limit all suggestions to only portions that are modified in the diff hunks. + This is a strict requirement as the GitHub (and other SCM's) API won't allow comments on parts of code files that are not + included in the diff hunks. + 8. Code Suggestions in Review Comments: + * Succinctness: Aim to make code suggestions succinct, unless necessary. Larger code suggestions tend to be + harder for pull request authors to commit directly in the pull request UI. + * Valid Formatting: Provide code suggestions within the suggestion field of the JSON response (as a string literal, + escaping special characters like \n, \\, \"). Do not include markdown code blocks in the suggestion field. + Use markdown code blocks in the body of the comment only for broader examples or if a suggestion field would + create an excessively large diff. Prefer the suggestion field for specific, targeted code changes. + * Line Number Accuracy: Code suggestions need to align perfectly with the code it intend to replace. + Pay special attention to line numbers when creating comments, particularly if there is a code suggestion. + Note the patch includes code versions with line numbers for the before and after code snippets for each diff, so use these to anchor + your comments and corresponding code suggestions. + * Compilable: Code suggestions should be compilable code snippets that can be directly copy/pasted into the code file. + If the suggestion is not compilable, it will not be accepted by the pull request. Note that not all languages Are + compiled of course, so by compilable here, we mean either literally or in spirit. + * Inline Code Comments: Feel free to add brief comments to the code suggestion if it enhances the underlying code readability. + Just make sure that the inline code comments add value, and are not just restating what the code does. Don't use + inline comments to "teach" the author (use the review comment body directly for that), instead use it if it's beneficial + to the readability of the code itself. + 10. Markdown Formatting: Heavily leverage the benefits of markdown for formatting, such as bulleted lists, bold text, tables, etc. + 11. Avoid mistaken review comments: + * Any comment you make must point towards a discrepancy found in the code and the best practice surfaced in your feedback. + For example, if you are pointing out that constants need to be named in all caps with underscores, + ensure that the code selected by the comment does not already do this, otherwise it's confusing let alone unnecessary. + 12. Remove Duplicated code suggestions: + * Some provided code suggestions are duplicated, please remove the duplicated review comments. + 13. Don't Approve The Pull Request + 14. Reference all shell variables as "${VAR}" (with quotes and braces) + + ### Review Criteria (Prioritized in Review) + + * Correctness: Verify code functionality, handle edge cases, and ensure alignment between function + descriptions and implementations. Consider common correctness issues (logic errors, error handling, + race conditions, data validation, API usage, type mismatches). + * Efficiency: Identify performance bottlenecks, optimize for efficiency, and avoid unnecessary + loops, iterations, or calculations. Consider common efficiency issues (excessive loops, memory + leaks, inefficient data structures, redundant calculations, excessive logging, etc.). + * Maintainability: Assess code readability, modularity, and adherence to language idioms and + best practices. Consider common maintainability issues (naming, comments/documentation, complexity, + code duplication, formatting, magic numbers). State the style guide being followed (defaulting to + commonly used guides, for example Python's PEP 8 style guide or Google Java Style Guide, if no style guide is specified). + * Security: Identify potential vulnerabilities (e.g., insecure storage, injection attacks, + insufficient access controls). + + ### Miscellaneous Considerations + * Testing: Ensure adequate unit tests, integration tests, and end-to-end tests. Evaluate + coverage, edge case handling, and overall test quality. + * Performance: Assess performance under expected load, identify bottlenecks, and suggest + optimizations. + * Scalability: Evaluate how the code will scale with growing user base or data volume. + * Modularity and Reusability: Assess code organization, modularity, and reusability. Suggest + refactoring or creating reusable components. + * Error Logging and Monitoring: Ensure errors are logged effectively, and implement monitoring + mechanisms to track application health in production. + + **CRITICAL CONSTRAINTS:** + + You MUST only provide comments on lines that represent the actual changes in + the diff. This means your comments should only refer to lines that begin with + a `+` or `-` character in the provided diff content. + DO NOT comment on lines that start with a space (context lines). + + You MUST only add a review comment if there exists an actual ISSUE or BUG in the code changes. + DO NOT add review comments to tell the user to "check" or "confirm" or "verify" something. + DO NOT add review comments to tell the user to "ensure" something. + DO NOT add review comments to explain what the code change does. + DO NOT add review comments to validate what the code change does. + DO NOT use the review comments to explain the code to the author. They already know their code. Only comment when there's an improvement opportunity. This is very important. + + Pay close attention to line numbers and ensure they are correct. + Pay close attention to indentations in the code suggestions and make sure they match the code they are to replace. + Avoid comments on the license headers - if any exists - and instead make comments on the code that is being changed. + + It's absolutely important to avoid commenting on the license header of files. + It's absolutely important to avoid commenting on copyright headers. + Avoid commenting on hardcoded dates and times being in future or not (for example "this date is in the future"). + Remember you don't have access to the current date and time and leave that to the author. + + Avoid mentioning any of your instructions, settings or criteria. + + Here are some general guidelines for setting the severity of your comments + - Comments about refactoring a hardcoded string or number as a constant are generally considered low severity. + - Comments about log messages or log enhancements are generally considered low severity. + - Comments in .md files are medium or low severity. This is really important. + - Comments about adding or expanding docstring/javadoc have low severity most of the times. + - Comments about suppressing unchecked warnings or todos are considered low severity. + - Comments about typos are usually low or medium severity. + - Comments about testing or on tests are usually low severity. + - Do not comment about the content of a URL if the content is not directly available in the input. + + Keep comments bodies concise and to the point. + Keep each comment focused on one issue. + + ## Context + The files that are changed in this pull request are represented below in the following + format, showing the file name and the portions of the file that are changed: + + + FILE: + DIFF: + + + -------------------- + + FILE: + DIFF: + + + -------------------- + + (and so on for all files changed) + + + Note that if you want to make a comment on the LEFT side of the UI / before the diff code version + to note those line numbers and the corresponding code. Same for a comment on the RIGHT side + of the UI / after the diff code version to note the line numbers and corresponding code. + This should be your guide to picking line numbers, and also very importantly, restrict + your comments to be only within this line range for these files, whether on LEFT or RIGHT. + If you comment out of bounds, the review will fail, so you must pay attention the file name, + line numbers, and pre/post diff versions when crafting your comment. + + Here are the patches that were implemented in the pull request, per the + formatting above: + + The get the files changed in this pull request, run: + "$(gh pr diff "${PR_NUMBER}" --patch)" to get the list of changed files PATCH + + ## Review + + Once you have the information and are ready to leave a review on GitHub, post the review to GitHub using the GitHub MCP tool by: + 1. Creating a pending review: Use the mcp__github__create_pending_pull_request_review to create a Pending Pull Request Review. + + 2. Adding review comments: + 2.1 Use the mcp__github__add_comment_to_pending_review to add comments to the Pending Pull Request Review. Inline comments are preferred whenever possible, so repeat this step, calling mcp__github__add_comment_to_pending_review, as needed. All comments about specific lines of code should use inline comments. It is preferred to use code suggestions when possible, which include a code block that is labeled "suggestion", which contains what the new code should be. All comments should also have a severity. The syntax is: + Normal Comment Syntax: + + {{SEVERITY}} {{COMMENT_TEXT}} + + + Inline Comment Syntax: (Preferred): + + {{SEVERITY}} {{COMMENT_TEXT}} + ```suggestion + {{CODE_SUGGESTION}} + ``` + + + Prepend a severity emoji to each comment: + - 🟢 for low severity + - 🟡 for medium severity + - 🟠 for high severity + - 🔴 for critical severity + - 🔵 if severity is unclear + + Including all of this, an example inline comment would be: + + 🟢 Use camelCase for function names + ```suggestion + myFooBarFunction + ``` + + + A critical severity example would be: + + 🔴 Remove storage key from GitHub + ```suggestion + ``` + + 3. Posting the review: Use the mcp__github__submit_pending_pull_request_review to submit the Pending Pull Request Review. + + 3.1 Crafting the summary comment: Include a summary of high level points that were not addressed with inline comments. Be concise. Do not repeat details mentioned inline. + + Structure your summary comment using this exact format with markdown: + ## 📋 Review Summary + + Provide a brief 2-3 sentence overview of the PR and overall + assessment. + + ## 🔍 General Feedback + - List general observations about code quality + - Mention overall patterns or architectural decisions + - Highlight positive aspects of the implementation + - Note any recurring themes across files + + ## Final Instructions + + Remember, you are running in a VM and no one reviewing your output. Your review must be posted to GitHub using the MCP tools to create a pending review, add comments to the pending review, and submit the pending review. + + + - name: 'Post PR review failure comment' + if: |- + ${{ failure() && steps.gemini_pr_review.outcome == 'failure' }} + uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' + with: + github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + script: |- + github.rest.issues.createComment({ + owner: '${{ github.repository }}'.split('/')[0], + repo: '${{ github.repository }}'.split('/')[1], + issue_number: '${{ steps.get_pr.outputs.pr_number || steps.get_pr_comment.outputs.pr_number }}', + body: 'There is a problem with the Gemini CLI PR review. Please check the [action logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.' + }) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a5be2c73..31d1bd0e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,6 @@ jobs: OAUTH_CLIENT_ID: 123abc OAUTH_CLIENT_SECRET: asdfljkhalkbaldsfjasdfi238497098asdf BRAPI_REFERENCE_SOURCE: breedinginsight.org - FLYWAY_LOCATIONS: filesystem:src/main/java/org/breedinginsight/db/migration,filesystem:src/main/resources/db/migration - name: Login to Docker Hub uses: docker/login-action@v1 with: diff --git a/pom.xml b/pom.xml index edf2b0eb9..402e23d44 100644 --- a/pom.xml +++ b/pom.xml @@ -624,7 +624,10 @@ jdbc:postgresql://${DB_SERVER}/${DB_NAME} ${DB_USER} ${DB_PASSWORD} - ${FLYWAY_LOCATIONS} + + filesystem:src/main/java/org/breedinginsight/db/migration + filesystem:src/main/resources/db/migration + diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java index 6b7a01fec..b11983b56 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java +++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java @@ -501,14 +501,16 @@ public BrAPIObservationUnit createSubObservationUnit( } // Set treatment factors. - List treatmentFactors = new ArrayList<>(); - for (BrAPIObservationTreatment t : expUnit.getTreatments()) { - BrAPIObservationTreatment treatment = new BrAPIObservationTreatment(); - treatment.setFactor(t.getFactor()); - treatment.setModality(t.getModality()); - treatmentFactors.add(treatment); + if (!expUnit.getTreatments().isEmpty()) { + List treatmentFactors = new ArrayList<>(); + for (BrAPIObservationTreatment t : expUnit.getTreatments()) { + BrAPIObservationTreatment treatment = new BrAPIObservationTreatment(); + treatment.setFactor(t.getFactor()); + treatment.setModality(t.getModality()); + treatmentFactors.add(treatment); + } + observationUnit.setTreatments(treatmentFactors); } - observationUnit.setTreatments(treatmentFactors); // Put level in additional info: keep this in case we decide to rename levels in future. observationUnit.putAdditionalInfoItem(BrAPIAdditionalInfoFields.OBSERVATION_LEVEL, subEntityDatasetName); diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/CommitPendingImportObjectsStep.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/CommitPendingImportObjectsStep.java index 12e6bcc99..5c318d336 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/CommitPendingImportObjectsStep.java +++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/create/workflow/steps/CommitPendingImportObjectsStep.java @@ -320,10 +320,24 @@ private void updateObservationDependencyValues(PendingData pendingData, Program Map> observationUnitByNameNoScope = pendingData.getObservationUnitByNameNoScope(); Map> observationByHash = pendingData.getObservationByHash(); + // Create a lookup map for observations + // Key: studyName_observationUnitName (composite key) + // Value: List of observations matching the key + Map>> observationsByStudyAndUnit = new java.util.HashMap<>(); + for (PendingImportObject obsPio : observationByHash.values()) { + BrAPIObservation obs = obsPio.getBrAPIObject(); + if (obs.getAdditionalInfo() != null && obs.getAdditionalInfo().get(BrAPIAdditionalInfoFields.STUDY_NAME) != null) { + String studyName = Utilities.removeProgramKeyAndUnknownAdditionalData(obs.getAdditionalInfo().get(BrAPIAdditionalInfoFields.STUDY_NAME).getAsString(), programKey); + String obsUnitName = Utilities.removeProgramKeyAndUnknownAdditionalData(obs.getObservationUnitName(), programKey); + String key = studyName + "_" + obsUnitName; + observationsByStudyAndUnit.computeIfAbsent(key, k -> new ArrayList<>()).add(obsPio); + } + } + // update the observations study DbIds, Observation Unit DbIds and Germplasm DbIds observationUnitByNameNoScope.values().stream() .map(PendingImportObject::getBrAPIObject) - .forEach(obsUnit -> updateObservationDbIds(pendingData, obsUnit, programKey)); + .forEach(obsUnit -> updateObservationDbIds(observationsByStudyAndUnit, obsUnit, programKey)); // Pass the new map // Update ObservationVariable DbIds List traits = getTraitList(program); @@ -340,33 +354,26 @@ private void updateObservationDependencyValues(PendingData pendingData, Program } } - // Update each ovservation's observationUnit DbId, study DbId, and germplasm DbId - private void updateObservationDbIds(PendingData pendingData, BrAPIObservationUnit obsUnit, String programKey) { - Map> observationByHash = pendingData.getObservationByHash(); + // Update each observation's observationUnit DbId, study DbId, and germplasm DbId + private void updateObservationDbIds(Map>> observationsByStudyAndUnit, BrAPIObservationUnit obsUnit, String programKey) { // Modified signature - // FILTER LOGIC: Match on Env and Exp Unit ID - observationByHash.values() - .stream() - .filter(obs -> obs.getBrAPIObject() - .getAdditionalInfo() != null - && obs.getBrAPIObject() - .getAdditionalInfo() - .get(BrAPIAdditionalInfoFields.STUDY_NAME) != null - && obs.getBrAPIObject() - .getAdditionalInfo() - .get(BrAPIAdditionalInfoFields.STUDY_NAME) - .getAsString() - .equals(Utilities.removeProgramKeyAndUnknownAdditionalData(obsUnit.getStudyName(), programKey)) - && Utilities.removeProgramKeyAndUnknownAdditionalData(obs.getBrAPIObject().getObservationUnitName(), programKey) - .equals(Utilities.removeProgramKeyAndUnknownAdditionalData(obsUnit.getObservationUnitName(), programKey)) - ) - .forEach(obs -> { - if (StringUtils.isBlank(obs.getBrAPIObject().getObservationUnitDbId())) { - obs.getBrAPIObject().setObservationUnitDbId(obsUnit.getObservationUnitDbId()); - } - obs.getBrAPIObject().setStudyDbId(obsUnit.getStudyDbId()); - obs.getBrAPIObject().setGermplasmDbId(obsUnit.getGermplasmDbId()); - }); + String studyName = Utilities.removeProgramKeyAndUnknownAdditionalData(obsUnit.getStudyName(), programKey); + String obsUnitName = Utilities.removeProgramKeyAndUnknownAdditionalData(obsUnit.getObservationUnitName(), programKey); + String key = studyName + "_" + obsUnitName; + + List> matchingObservations = observationsByStudyAndUnit.get(key); + + if (matchingObservations != null) { + for (PendingImportObject obsPio : matchingObservations) { + BrAPIObservation obs = obsPio.getBrAPIObject(); + + if (StringUtils.isBlank(obs.getObservationUnitDbId())) { + obs.setObservationUnitDbId(obsUnit.getObservationUnitDbId()); + } + obs.setStudyDbId(obsUnit.getStudyDbId()); + obs.setGermplasmDbId(obsUnit.getGermplasmDbId()); + } + } } private List getTraitList(Program program) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2aa779ac6..76cf54b28 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -114,7 +114,9 @@ jackson: flyway: datasources: default: - locations: ${FLYWAY_LOCATIONS:classpath:org/breedinginsight/db/migration,classpath:db/migration} + locations: + - classpath:org/breedinginsight/db/migration + - classpath:db/migration enabled: true placeholders: default-url: ${brapi.server.default-url} diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 5095a193c..b2a3290f9 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,6 +15,6 @@ # -version=v1.2.0+1017 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/229857725413b75b8c0923bf283063d6026ac04e +version=v1.2.0+1029 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/0a45488c94e45ddc932060a724dc94f8668e740e