From 0a482f03c3f9b18e62be685ff8fa0469ee812023 Mon Sep 17 00:00:00 2001 From: "M. Morgan Aster" Date: Wed, 26 Feb 2025 14:49:52 -0500 Subject: [PATCH] TSPS-405 Add quota units to pipeline quotas (#205) --- common/openapi.yml | 9 +++++- .../app/controller/QuotasController.java | 10 +++++-- .../common/utils/QuotaUnitsEnum.java | 15 ++++++++++ .../pipelines/db/entities/PipelineQuota.java | 11 +++++++- .../converters/QuotaUnitsConverter.java | 19 +++++++++++++ .../PipelineQuotasRepository.java | 5 ++++ .../pipelines/service/QuotasService.java | 6 ++++ service/src/main/resources/db/changelog.xml | 1 + .../changesets/20250225_add_quota_units.yaml | 28 +++++++++++++++++++ .../controller/QuotasControllerTest.java | 4 +++ .../db/entities/PipelineQuotaTest.java | 5 +++- .../pipelines/service/QuotasServiceTest.java | 10 +++++++ 12 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 service/src/main/java/bio/terra/pipelines/common/utils/QuotaUnitsEnum.java create mode 100644 service/src/main/java/bio/terra/pipelines/db/entities/converters/QuotaUnitsConverter.java create mode 100644 service/src/main/resources/db/changesets/20250225_add_quota_units.yaml diff --git a/common/openapi.yml b/common/openapi.yml index 31d9d369..f0b5753e 100644 --- a/common/openapi.yml +++ b/common/openapi.yml @@ -932,11 +932,16 @@ components: Maximum allowable quota for the pipeline type: integer + QuotaUnits: + description: | + Units for pipeline quota + type: string + QuotaWithDetails: description: | Object containing the pipeline identifier, quota limit, and quota usage of a Pipeline. type: object - required: [ pipelineName, quotaLimit, quotaConsumed ] + required: [ pipelineName, quotaLimit, quotaConsumed, quotaUnits ] properties: pipelineName: $ref: "#/components/schemas/PipelineName" @@ -944,6 +949,8 @@ components: $ref: "#/components/schemas/QuotaLimit" quotaConsumed: $ref: "#/components/schemas/QuotaConsumed" + quotaUnits: + $ref: "#/components/schemas/QuotaUnits" StartPipelineRunRequestBody: description: | diff --git a/service/src/main/java/bio/terra/pipelines/app/controller/QuotasController.java b/service/src/main/java/bio/terra/pipelines/app/controller/QuotasController.java index adbc39dc..51a251a1 100644 --- a/service/src/main/java/bio/terra/pipelines/app/controller/QuotasController.java +++ b/service/src/main/java/bio/terra/pipelines/app/controller/QuotasController.java @@ -4,6 +4,7 @@ import bio.terra.common.iam.SamUserFactory; import bio.terra.pipelines.app.configuration.external.SamConfiguration; import bio.terra.pipelines.common.utils.PipelinesEnum; +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; import bio.terra.pipelines.db.entities.UserQuota; import bio.terra.pipelines.generated.api.QuotasApi; import bio.terra.pipelines.generated.model.ApiQuotaWithDetails; @@ -51,15 +52,18 @@ public ResponseEntity getQuotaForPipeline(String pipelineNa UserQuota userQuota = quotasService.getOrCreateQuotaForUserAndPipeline( user.getSubjectId(), validatedPipelineName); + QuotaUnitsEnum quotaUnits = quotasService.getQuotaUnitsForPipeline(validatedPipelineName); - return new ResponseEntity<>(quotasToApiQuotaWithDetails(userQuota), HttpStatus.OK); + return new ResponseEntity<>(quotasToApiQuotaWithDetails(userQuota, quotaUnits), HttpStatus.OK); } - static ApiQuotaWithDetails quotasToApiQuotaWithDetails(UserQuota userQuota) { + static ApiQuotaWithDetails quotasToApiQuotaWithDetails( + UserQuota userQuota, QuotaUnitsEnum quotaUnits) { return new ApiQuotaWithDetails() .pipelineName(userQuota.getPipelineName().getValue()) .quotaConsumed(userQuota.getQuotaConsumed()) - .quotaLimit(userQuota.getQuota()); + .quotaLimit(userQuota.getQuota()) + .quotaUnits(quotaUnits.getValue()); } } diff --git a/service/src/main/java/bio/terra/pipelines/common/utils/QuotaUnitsEnum.java b/service/src/main/java/bio/terra/pipelines/common/utils/QuotaUnitsEnum.java new file mode 100644 index 00000000..09272139 --- /dev/null +++ b/service/src/main/java/bio/terra/pipelines/common/utils/QuotaUnitsEnum.java @@ -0,0 +1,15 @@ +package bio.terra.pipelines.common.utils; + +public enum QuotaUnitsEnum { + SAMPLES("samples"); + + private final String value; + + QuotaUnitsEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/service/src/main/java/bio/terra/pipelines/db/entities/PipelineQuota.java b/service/src/main/java/bio/terra/pipelines/db/entities/PipelineQuota.java index f1eec011..53c2d5cf 100644 --- a/service/src/main/java/bio/terra/pipelines/db/entities/PipelineQuota.java +++ b/service/src/main/java/bio/terra/pipelines/db/entities/PipelineQuota.java @@ -1,6 +1,7 @@ package bio.terra.pipelines.db.entities; import bio.terra.pipelines.common.utils.PipelinesEnum; +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @@ -28,9 +29,17 @@ public class PipelineQuota { @Column(name = "min_quota_consumed") private int minQuotaConsumed; - public PipelineQuota(PipelinesEnum pipelineName, int defaultQuota, int minQuotaConsumed) { + @Column(name = "quota_units") + private QuotaUnitsEnum quotaUnits; + + public PipelineQuota( + PipelinesEnum pipelineName, + int defaultQuota, + int minQuotaConsumed, + QuotaUnitsEnum quotaUnits) { this.pipelineName = pipelineName; this.defaultQuota = defaultQuota; this.minQuotaConsumed = minQuotaConsumed; + this.quotaUnits = quotaUnits; } } diff --git a/service/src/main/java/bio/terra/pipelines/db/entities/converters/QuotaUnitsConverter.java b/service/src/main/java/bio/terra/pipelines/db/entities/converters/QuotaUnitsConverter.java new file mode 100644 index 00000000..c8fc2db7 --- /dev/null +++ b/service/src/main/java/bio/terra/pipelines/db/entities/converters/QuotaUnitsConverter.java @@ -0,0 +1,19 @@ +package bio.terra.pipelines.db.entities.converters; + +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +// inspired by https://www.baeldung.com/jpa-persisting-enums-in-jpa +@Converter(autoApply = true) +public class QuotaUnitsConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(QuotaUnitsEnum quotaUnitsEnum) { + return quotaUnitsEnum.getValue(); + } + + @Override + public QuotaUnitsEnum convertToEntityAttribute(String quotaUnitsString) { + return QuotaUnitsEnum.valueOf(quotaUnitsString.toUpperCase()); + } +} diff --git a/service/src/main/java/bio/terra/pipelines/db/repositories/PipelineQuotasRepository.java b/service/src/main/java/bio/terra/pipelines/db/repositories/PipelineQuotasRepository.java index 05940bb9..27710b1c 100644 --- a/service/src/main/java/bio/terra/pipelines/db/repositories/PipelineQuotasRepository.java +++ b/service/src/main/java/bio/terra/pipelines/db/repositories/PipelineQuotasRepository.java @@ -1,9 +1,14 @@ package bio.terra.pipelines.db.repositories; import bio.terra.pipelines.common.utils.PipelinesEnum; +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; import bio.terra.pipelines.db.entities.PipelineQuota; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; public interface PipelineQuotasRepository extends CrudRepository { PipelineQuota findByPipelineName(PipelinesEnum pipelineName); + + @Query("SELECT p.quotaUnits FROM PipelineQuota p WHERE p.pipelineName = ?1") + QuotaUnitsEnum findQuotaUnitsByPipelineName(PipelinesEnum pipelineName); } diff --git a/service/src/main/java/bio/terra/pipelines/service/QuotasService.java b/service/src/main/java/bio/terra/pipelines/service/QuotasService.java index 4a5d8b83..77bbae3a 100644 --- a/service/src/main/java/bio/terra/pipelines/service/QuotasService.java +++ b/service/src/main/java/bio/terra/pipelines/service/QuotasService.java @@ -3,6 +3,7 @@ import bio.terra.common.db.WriteTransaction; import bio.terra.common.exception.InternalServerErrorException; import bio.terra.pipelines.common.utils.PipelinesEnum; +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; import bio.terra.pipelines.db.entities.PipelineQuota; import bio.terra.pipelines.db.entities.UserQuota; import bio.terra.pipelines.db.repositories.PipelineQuotasRepository; @@ -33,6 +34,11 @@ public PipelineQuota getPipelineQuota(PipelinesEnum pipelineName) { return pipelineQuotasRepository.findByPipelineName(pipelineName); } + /** This method gets the quota units value for a given pipeline. */ + public QuotaUnitsEnum getQuotaUnitsForPipeline(PipelinesEnum pipelineName) { + return pipelineQuotasRepository.findQuotaUnitsByPipelineName(pipelineName); + } + /** * This method gets the quota for a given user and pipeline. If the user quota does not exist, it * will create a new row in the user quotas table with the default quota for the pipeline. diff --git a/service/src/main/resources/db/changelog.xml b/service/src/main/resources/db/changelog.xml index ba6cbce4..1542a137 100644 --- a/service/src/main/resources/db/changelog.xml +++ b/service/src/main/resources/db/changelog.xml @@ -11,6 +11,7 @@ + diff --git a/service/src/main/resources/db/changesets/20250225_add_quota_units.yaml b/service/src/main/resources/db/changesets/20250225_add_quota_units.yaml new file mode 100644 index 00000000..6753944f --- /dev/null +++ b/service/src/main/resources/db/changesets/20250225_add_quota_units.yaml @@ -0,0 +1,28 @@ +# add quota_units field to pipeline_quotas table and set value for array_imputation pipeline + +databaseChangeLog: + - changeSet: + id: add and populate quota_units column in pipeline_quotas table + author: mma + changes: + # add quota_units column to pipeline_quotas table + - addColumn: + tableName: pipeline_quotas + columns: + - column: + name: quota_units + type: text + constraints: + nullable: true + # update quota_units value for array_imputation pipeline + - update: + tableName: pipeline_quotas + columns: + - column: + name: quota_units + value: 'samples' + where: pipeline_name='array_imputation' + # make quota_units non-nullable + - addNotNullConstraint: + tableName: pipeline_quotas + columnName: quota_units diff --git a/service/src/test/java/bio/terra/pipelines/controller/QuotasControllerTest.java b/service/src/test/java/bio/terra/pipelines/controller/QuotasControllerTest.java index 032b8b0c..896651e9 100644 --- a/service/src/test/java/bio/terra/pipelines/controller/QuotasControllerTest.java +++ b/service/src/test/java/bio/terra/pipelines/controller/QuotasControllerTest.java @@ -15,6 +15,7 @@ import bio.terra.pipelines.app.controller.GlobalExceptionHandler; import bio.terra.pipelines.app.controller.QuotasController; import bio.terra.pipelines.common.utils.PipelinesEnum; +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; import bio.terra.pipelines.db.entities.UserQuota; import bio.terra.pipelines.db.exception.InvalidPipelineException; import bio.terra.pipelines.generated.model.ApiQuotaWithDetails; @@ -52,6 +53,8 @@ void beforeEach() { when(quotasServiceMock.getOrCreateQuotaForUserAndPipeline( testUser.getSubjectId(), PipelinesEnum.ARRAY_IMPUTATION)) .thenReturn(testUserQuota); + when(quotasServiceMock.getQuotaUnitsForPipeline(PipelinesEnum.ARRAY_IMPUTATION)) + .thenReturn(QuotaUnitsEnum.SAMPLES); } @Test @@ -71,6 +74,7 @@ void getQuotaOk() throws Exception { assertEquals(testUserQuota.getQuota(), response.getQuotaLimit()); assertEquals(testUserQuota.getQuotaConsumed(), response.getQuotaConsumed()); assertEquals(testUserQuota.getPipelineName().getValue(), response.getPipelineName()); + assertEquals(QuotaUnitsEnum.SAMPLES.getValue(), response.getQuotaUnits()); } @Test diff --git a/service/src/test/java/bio/terra/pipelines/db/entities/PipelineQuotaTest.java b/service/src/test/java/bio/terra/pipelines/db/entities/PipelineQuotaTest.java index 40bfc399..9a1d07d7 100644 --- a/service/src/test/java/bio/terra/pipelines/db/entities/PipelineQuotaTest.java +++ b/service/src/test/java/bio/terra/pipelines/db/entities/PipelineQuotaTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import bio.terra.pipelines.common.utils.PipelinesEnum; +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; import bio.terra.pipelines.testutils.BaseTest; import org.junit.jupiter.api.Test; @@ -10,9 +11,11 @@ class PipelineQuotaTest extends BaseTest { @Test void testPipelineQuotaConstructor() { - PipelineQuota pipelineQuota = new PipelineQuota(PipelinesEnum.ARRAY_IMPUTATION, 10000, 500); + PipelineQuota pipelineQuota = + new PipelineQuota(PipelinesEnum.ARRAY_IMPUTATION, 10000, 500, QuotaUnitsEnum.SAMPLES); assertEquals(PipelinesEnum.ARRAY_IMPUTATION, pipelineQuota.getPipelineName()); assertEquals(10000, pipelineQuota.getDefaultQuota()); assertEquals(500, pipelineQuota.getMinQuotaConsumed()); + assertEquals(QuotaUnitsEnum.SAMPLES, pipelineQuota.getQuotaUnits()); } } diff --git a/service/src/test/java/bio/terra/pipelines/service/QuotasServiceTest.java b/service/src/test/java/bio/terra/pipelines/service/QuotasServiceTest.java index 30503947..9842e253 100644 --- a/service/src/test/java/bio/terra/pipelines/service/QuotasServiceTest.java +++ b/service/src/test/java/bio/terra/pipelines/service/QuotasServiceTest.java @@ -4,6 +4,7 @@ import bio.terra.common.exception.InternalServerErrorException; import bio.terra.pipelines.common.utils.PipelinesEnum; +import bio.terra.pipelines.common.utils.QuotaUnitsEnum; import bio.terra.pipelines.db.entities.PipelineQuota; import bio.terra.pipelines.db.entities.UserQuota; import bio.terra.pipelines.db.repositories.PipelineQuotasRepository; @@ -25,6 +26,15 @@ void getPipelineQuota() { assertEquals(PipelinesEnum.ARRAY_IMPUTATION, pipelineQuota.getPipelineName()); assertEquals(10000, pipelineQuota.getDefaultQuota()); assertEquals(500, pipelineQuota.getMinQuotaConsumed()); + assertEquals(QuotaUnitsEnum.SAMPLES, pipelineQuota.getQuotaUnits()); + } + + @Test + void getQuotaUnitsForPipeline() { + QuotaUnitsEnum quotaUnits = + quotasService.getQuotaUnitsForPipeline(PipelinesEnum.ARRAY_IMPUTATION); + + assertEquals(QuotaUnitsEnum.SAMPLES, quotaUnits); } @Test