Skip to content

Commit

Permalink
Add created, created_by columns to artifacts (#305)
Browse files Browse the repository at this point in the history
* cohort created

* created concept set

* created review study

* last modified, some cohort created_by

* finish cohort created_by

* concept set created_by

* review created_by

* study created_by

* move toApiObject back to controller

* spotbugs

* fix ui tests

* review fixes

* checkstyle
  • Loading branch information
melissachang authored Dec 22, 2022
1 parent 9be7339 commit f1b1281
Show file tree
Hide file tree
Showing 20 changed files with 422 additions and 54 deletions.
22 changes: 22 additions & 0 deletions .run/Main.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Main" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
<module name="tanagra.service.main" />
<option name="SPRING_BOOT_MAIN_CLASS" value="bio.terra.tanagra.app.Main" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/service" />
<option name="ALTERNATIVE_JRE_PATH" />
<envs>
<env name="TANAGRA_DATABASE_NAME" value="tanagra_db" />
<env name="TANAGRA_DB_INITIALIZE_ON_START" value="false" />
<env name="TANAGRA_DB_USERNAME" value="dbuser" />
<env name="TANAGRA_DB_PASSWORD" value="dbpwd" />
<env name="TANAGRA_UNDERLAY_FILES" value="broad/aou_synthetic/expanded/aou_synthetic.json,broad/cms_synpuf/expanded/cms_synpuf.json" />
<env name="TANAGRA_FEATURE_ARTIFACT_STORAGE_ENABLED" value="true" />
<env name="TANAGRA_AUTH_IAP_GKE_JWT" value="false" />
<env name="TANAGRA_AUTH_BEARER_TOKEN" value="true" />
<env name="GOOGLE_APPLICATION_CREDENTIALS" value="$PROJECT_DIR$/rendered/tanagra_sa.json" />
</envs>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public ResponseEntity<ApiCohortV2> createCohort(String studyId, ApiCohortCreateI
.underlayName(body.getUnderlayName())
.cohortRevisionGroupId(newCohortRevisionGroupId)
.version(Cohort.STARTING_VERSION)
.createdBy(UserId.currentUser().getEmail())
.displayName(body.getDisplayName())
.description(body.getDescription())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public ResponseEntity<ApiConceptSetV2> createConceptSet(
.conceptSetId(newConceptSetId)
.underlayName(body.getUnderlayName())
.entityName(body.getEntity())
.createdBy(UserId.currentUser().getEmail())
.displayName(body.getDisplayName())
.description(body.getDescription())
.build();
Expand Down Expand Up @@ -153,15 +154,16 @@ public ResponseEntity<ApiConceptSetV2> updateConceptSet(
return ResponseEntity.ok(toApiObject(updatedConceptSet));
}

/** Convert the internal Concept Set object to an API Concept Set object. */
private static ApiConceptSetV2 toApiObject(ConceptSet conceptSet) {
return new ApiConceptSetV2()
.id(conceptSet.getConceptSetId())
.underlayName(conceptSet.getUnderlayName())
.entity(conceptSet.getEntityName())
.displayName(conceptSet.getDisplayName())
.description(conceptSet.getDescription())
.lastModified(conceptSet.getLastModifiedUTC())
.created(conceptSet.getCreated())
.createdBy(conceptSet.getCreatedBy())
.lastModified(conceptSet.getLastModified())
.criteria(
conceptSet.getCriteria() == null
? null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public ResponseEntity<ApiReviewV2> createReview(
.displayName(body.getDisplayName())
.description(body.getDescription())
.size(body.getSize())
.createdBy(UserId.currentUser().getEmail())
.build();

// TODO: Move this to the ReviewService once we can build the EntityFilter from the Cohort on
Expand Down Expand Up @@ -309,7 +310,9 @@ private static ApiReviewV2 toApiObject(Review review) {
.displayName(review.getDisplayName())
.description(review.getDescription())
.size(review.getSize())
.created(review.getCreatedUTC())
.created(review.getCreated())
.createdBy(review.getCreatedBy())
.lastModified(review.getLastModified())
.cohort(ToApiConversionUtils.toApiObject(review.getCohort()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public ResponseEntity<ApiStudyV2> createStudy(ApiStudyCreateInfoV2 body) {
.displayName(body.getDisplayName())
.description(body.getDescription())
.properties(fromApiObject(body.getProperties()))
.createdBy(UserId.currentUser().getEmail())
.build();
studyService.createStudy(studyToCreate);
return ResponseEntity.ok(toApiObject(studyToCreate));
Expand Down Expand Up @@ -133,7 +134,10 @@ private static ApiStudyV2 toApiObject(Study study) {
.id(study.getStudyId())
.displayName(study.getDisplayName())
.description(study.getDescription())
.properties(apiProperties);
.properties(apiProperties)
.created(study.getCreated())
.createdBy(study.getCreatedBy())
.lastModified(study.getLastModified());
}

private static ImmutableMap<String, String> fromApiObject(
Expand Down
12 changes: 8 additions & 4 deletions service/src/main/java/bio/terra/tanagra/db/CohortDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class CohortDao {

// SQL query and row mapper for reading a cohort.
private static final String COHORT_SELECT_SQL =
"SELECT study_id, cohort_id, underlay_name, cohort_revision_group_id, version, is_most_recent, is_editable, last_modified, display_name, description FROM cohort";
"SELECT study_id, cohort_id, underlay_name, cohort_revision_group_id, version, is_most_recent, is_editable, created, created_by, last_modified, display_name, description FROM cohort";
private static final RowMapper<Cohort.Builder> COHORT_ROW_MAPPER =
(rs, rowNum) ->
Cohort.builder()
Expand All @@ -49,7 +49,9 @@ public class CohortDao {
.version(rs.getInt("version"))
.isMostRecent(rs.getBoolean("is_most_recent"))
.isEditable(rs.getBoolean("is_editable"))
.lastModified(rs.getTimestamp("last_modified"))
.createdBy(rs.getString("created_by"))
.created(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("created")))
.lastModified(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("last_modified")))
.displayName(rs.getString("display_name"))
.description(rs.getString("description"));

Expand Down Expand Up @@ -342,15 +344,17 @@ public boolean updateCohortLatestVersion(
private void createCohortHelper(Cohort cohort) {
// Store the cohort. New cohort rows are always the most recent and editable.
final String cohortSql =
"INSERT INTO cohort (study_id, cohort_id, underlay_name, cohort_revision_group_id, version, is_most_recent, is_editable, last_modified, display_name, description) "
+ "VALUES (:study_id, :cohort_id, :underlay_name, :cohort_revision_group_id, :version, TRUE, TRUE, :last_modified, :display_name, :description)";
"INSERT INTO cohort (study_id, cohort_id, underlay_name, cohort_revision_group_id, version, is_most_recent, is_editable, created_by, last_modified, display_name, description) "
+ "VALUES (:study_id, :cohort_id, :underlay_name, :cohort_revision_group_id, :version, TRUE, TRUE, :created_by, :last_modified, :display_name, :description)";
MapSqlParameterSource params =
new MapSqlParameterSource()
.addValue("study_id", cohort.getStudyId())
.addValue("cohort_id", cohort.getCohortId())
.addValue("underlay_name", cohort.getUnderlayName())
.addValue("cohort_revision_group_id", cohort.getCohortRevisionGroupId())
.addValue("version", cohort.getVersion())
// Don't need to set created. Liquibase defaultValueComputed handles that.
.addValue("created_by", cohort.getCreatedBy())
.addValue("last_modified", Timestamp.from(Instant.now()))
.addValue("display_name", cohort.getDisplayName())
.addValue("description", cohort.getDescription());
Expand Down
12 changes: 8 additions & 4 deletions service/src/main/java/bio/terra/tanagra/db/ConceptSetDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ public class ConceptSetDao {

// SQL query and row mapper for reading a concept set.
private static final String CONCEPT_SET_SELECT_SQL =
"SELECT study_id, concept_set_id, underlay_name, entity_name, last_modified, display_name, description FROM concept_set";
"SELECT study_id, concept_set_id, underlay_name, entity_name, created, created_by, last_modified, display_name, description FROM concept_set";
private static final RowMapper<ConceptSet.Builder> CONCEPT_SET_ROW_MAPPER =
(rs, rowNum) ->
ConceptSet.builder()
.studyId(rs.getString("study_id"))
.conceptSetId(rs.getString("concept_set_id"))
.underlayName(rs.getString("underlay_name"))
.entityName(rs.getString("entity_name"))
.lastModified(rs.getTimestamp("last_modified"))
.created(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("created")))
.createdBy(rs.getString("created_by"))
.lastModified(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("last_modified")))
.displayName(rs.getString("display_name"))
.description(rs.getString("description"));

Expand Down Expand Up @@ -170,14 +172,16 @@ private void populateCriteria(List<ConceptSet.Builder> conceptSets) {
public void createConceptSet(ConceptSet conceptSet) {
// Store the concept set.
final String sql =
"INSERT INTO concept_set (study_id, concept_set_id, underlay_name, entity_name, last_modified, display_name, description) "
+ "VALUES (:study_id, :concept_set_id, :underlay_name, :entity_name, :last_modified, :display_name, :description)";
"INSERT INTO concept_set (study_id, concept_set_id, underlay_name, entity_name, created_by, last_modified, display_name, description) "
+ "VALUES (:study_id, :concept_set_id, :underlay_name, :entity_name, :created_by, :last_modified, :display_name, :description)";
MapSqlParameterSource params =
new MapSqlParameterSource()
.addValue("study_id", conceptSet.getStudyId())
.addValue("concept_set_id", conceptSet.getConceptSetId())
.addValue("underlay_name", conceptSet.getUnderlayName())
.addValue("entity_name", conceptSet.getEntityName())
// Don't need to set created. Liquibase defaultValueComputed handles that.
.addValue("created_by", conceptSet.getCreatedBy())
.addValue("last_modified", Timestamp.from(Instant.now()))
.addValue("display_name", conceptSet.getDisplayName())
.addValue("description", conceptSet.getDescription());
Expand Down
7 changes: 7 additions & 0 deletions service/src/main/java/bio/terra/tanagra/db/DbUtils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package bio.terra.tanagra.db;

import bio.terra.common.exception.MissingRequiredFieldException;
import java.sql.Timestamp;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -39,4 +42,8 @@ public static String setColumnsClause(MapSqlParameterSource columnParams, String

return sb.toString();
}

public static OffsetDateTime timestampToOffsetDateTime(Timestamp timestamp) {
return OffsetDateTime.ofInstant(timestamp.toInstant(), ZoneId.of("UTC"));
}
}
15 changes: 8 additions & 7 deletions service/src/main/java/bio/terra/tanagra/db/ReviewDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import bio.terra.tanagra.query.RowResult;
import bio.terra.tanagra.service.artifact.Cohort;
import bio.terra.tanagra.service.artifact.Review;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
Expand All @@ -37,7 +35,7 @@ public class ReviewDao {

// SQL query and row mapper for reading a review.
private static final String REVIEW_SELECT_SQL =
"SELECT r.cohort_id, r.review_id, r.display_name, r.description, r.size, r.created FROM review AS r "
"SELECT r.cohort_id, r.review_id, r.display_name, r.description, r.size, r.created, r.created_by, r.last_modified FROM review AS r "
+ "JOIN cohort AS c ON c.cohort_id = r.cohort_id";
private static final RowMapper<Review.Builder> REVIEW_ROW_MAPPER =
(rs, rowNum) ->
Expand All @@ -47,7 +45,9 @@ public class ReviewDao {
.displayName(rs.getString("display_name"))
.description(rs.getString("description"))
.size(rs.getInt("size"))
.created(rs.getTimestamp("created"));
.created(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("created")))
.createdBy(rs.getString("created_by"))
.lastModified(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("last_modified")));

// SQL query and row mapper for reading a review instance.
private static final String REVIEW_INSTANCE_SELECT_SQL =
Expand Down Expand Up @@ -79,16 +79,17 @@ public void createReview(
cohortDao.freezeCohortLatestVersionOrThrow(studyId, cohort.getCohortRevisionGroupId());

final String sql =
"INSERT INTO review (cohort_id, review_id, display_name, description, size, created) "
+ "VALUES (:cohort_id, :review_id, :display_name, :description, :size, :created)";
"INSERT INTO review (cohort_id, review_id, display_name, description, size, created, created_by) "
+ "VALUES (:cohort_id, :review_id, :display_name, :description, :size, :created, :created_by)";
MapSqlParameterSource params =
new MapSqlParameterSource()
.addValue("cohort_id", cohort.getCohortId())
.addValue("review_id", review.getReviewId())
.addValue("display_name", review.getDisplayName())
.addValue("description", review.getDescription())
.addValue("size", review.getSize())
.addValue("created", Timestamp.from(Instant.now()));
// Don't need to set created. Liquibase defaultValueComputed handles that.
.addValue("created_by", review.getCreatedBy());
try {
jdbcTemplate.update(sql, params);
LOGGER.info("Inserted record for review {}", review.getReviewId());
Expand Down
13 changes: 9 additions & 4 deletions service/src/main/java/bio/terra/tanagra/db/StudyDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class StudyDao {

// SQL query and row mapper for reading a study.
private static final String STUDY_SELECT_SQL =
"SELECT study_id, display_name, description, properties FROM study";
"SELECT study_id, display_name, description, properties, created, created_by, last_modified FROM study";
private static final RowMapper<Study> STUDY_ROW_MAPPER =
(rs, rowNum) ->
Study.builder()
Expand All @@ -41,6 +41,9 @@ public class StudyDao {
Optional.ofNullable(rs.getString("properties"))
.map(DbSerDes::jsonToProperties)
.orElse(null))
.created(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("created")))
.createdBy(rs.getString("created_by"))
.lastModified(DbUtils.timestampToOffsetDateTime(rs.getTimestamp("last_modified")))
.build();

private final NamedParameterJdbcTemplate jdbcTemplate;
Expand All @@ -58,15 +61,17 @@ public StudyDao(NamedParameterJdbcTemplate jdbcTemplate) {
@WriteTransaction
public void createStudy(Study study) {
final String sql =
"INSERT INTO study (study_id, display_name, description, properties) "
+ "VALUES (:study_id, :display_name, :description, CAST(:properties AS jsonb))";
"INSERT INTO study (study_id, display_name, description, properties, created_by) "
+ "VALUES (:study_id, :display_name, :description, CAST(:properties AS jsonb), :created_by)";

MapSqlParameterSource params =
new MapSqlParameterSource()
.addValue("study_id", study.getStudyId())
.addValue("display_name", study.getDisplayName())
.addValue("description", study.getDescription())
.addValue("properties", DbSerDes.propertiesToJson(study.getProperties()));
.addValue("properties", DbSerDes.propertiesToJson(study.getProperties()))
// Don't need to set created. Liquibase defaultValueComputed handles that.
.addValue("created_by", study.getCreatedBy());
try {
jdbcTemplate.update(sql, params);
LOGGER.info("Inserted record for study {}", study.getStudyId());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package bio.terra.tanagra.service.artifact;

import bio.terra.tanagra.exception.SystemException;
import java.sql.Timestamp;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -19,7 +17,9 @@ public class Cohort {
private final int version;
private final boolean isMostRecent;
private final boolean isEditable;
private final Timestamp lastModified;
private final OffsetDateTime created;
private final String createdBy;
private final OffsetDateTime lastModified;
private final @Nullable String displayName;
private final @Nullable String description;
private final List<CriteriaGroup> criteriaGroups;
Expand All @@ -32,6 +32,8 @@ private Cohort(Builder builder) {
this.version = builder.version;
this.isMostRecent = builder.isMostRecent;
this.isEditable = builder.isEditable;
this.created = builder.created;
this.createdBy = builder.createdBy;
this.lastModified = builder.lastModified;
this.displayName = builder.displayName;
this.description = builder.description;
Expand All @@ -51,6 +53,8 @@ public Builder toBuilder() {
.version(version)
.isMostRecent(isMostRecent)
.isEditable(isEditable)
.created(created)
.createdBy(createdBy)
.lastModified(lastModified)
.displayName(displayName)
.description(description)
Expand Down Expand Up @@ -98,9 +102,19 @@ public boolean isEditable() {
return isEditable;
}

/** Timestamp of when this cohort was created. */
public OffsetDateTime getCreated() {
return created;
}

/** Email of user who created this cohort. */
public String getCreatedBy() {
return createdBy;
}

/** Timestamp of when this cohort was last modified. */
public OffsetDateTime getLastModifiedUTC() {
return lastModified.toInstant().atOffset(ZoneOffset.UTC);
public OffsetDateTime getLastModified() {
return lastModified;
}

/** Optional display name for the cohort. */
Expand Down Expand Up @@ -130,7 +144,9 @@ public static class Builder {
private int version;
private boolean isMostRecent;
private boolean isEditable;
private Timestamp lastModified;
private OffsetDateTime created;
private String createdBy;
private OffsetDateTime lastModified;
private @Nullable String displayName;
private @Nullable String description;
private List<CriteriaGroup> criteriaGroups = new ArrayList<>();
Expand Down Expand Up @@ -170,8 +186,18 @@ public Builder isEditable(boolean isEditable) {
return this;
}

public Builder lastModified(Timestamp lastModified) {
this.lastModified = (Timestamp) lastModified.clone();
public Builder created(OffsetDateTime created) {
this.created = created;
return this;
}

public Builder createdBy(String createdBy) {
this.createdBy = createdBy;
return this;
}

public Builder lastModified(OffsetDateTime lastModified) {
this.lastModified = lastModified;
return this;
}

Expand Down
Loading

0 comments on commit f1b1281

Please sign in to comment.