From 0dcad61872dff43c9e24e34883d2fce32dd63f94 Mon Sep 17 00:00:00 2001 From: akshat-sonic Date: Mon, 13 Feb 2023 23:59:00 +0530 Subject: [PATCH] add create metric api along with changes in ExperimentEntity --- litmus-core/pom.xml | 5 +++ .../controller/v1/MetricController.java | 12 +++++- .../com/navi/medici/mapper/MetricMapper.java | 26 +++++++++++++ .../medici/service/metric/MetricService.java | 8 ++++ .../service/metric/MetricServiceImpl.java | 38 +++++++++++++++++++ .../validator/MetricRequestValidator.java | 21 ++++++++++ litmus-db/pom.xml | 6 +++ .../navi/medici/entity/ExperimentEntity.java | 22 +++++++++++ .../com/navi/medici/entity/MetricEntity.java | 15 ++++++-- .../repository/ExperimentRepository.java | 2 + .../202302131535-add-metrics-table.sql | 2 +- ...-add-enhancement-columns-in-experiment.sql | 12 ++++++ .../request/v1/CreateMetricRequest.java | 20 ++++++++++ .../navi/medici/request/v1/LitmusOwner.java | 22 +++++++++++ .../navi/medici/response/MetricResponse.java | 30 +++++++++++++++ 15 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 litmus-core/src/main/java/com/navi/medici/mapper/MetricMapper.java create mode 100644 litmus-core/src/main/java/com/navi/medici/service/metric/MetricService.java create mode 100644 litmus-core/src/main/java/com/navi/medici/service/metric/MetricServiceImpl.java create mode 100644 litmus-core/src/main/java/com/navi/medici/validator/MetricRequestValidator.java create mode 100644 litmus-liquibase/src/main/resources/db/changelog/202302132341-add-enhancement-columns-in-experiment.sql create mode 100644 litmus-model/src/main/java/com/navi/medici/request/v1/CreateMetricRequest.java create mode 100644 litmus-model/src/main/java/com/navi/medici/request/v1/LitmusOwner.java create mode 100644 litmus-model/src/main/java/com/navi/medici/response/MetricResponse.java diff --git a/litmus-core/pom.xml b/litmus-core/pom.xml index b297098..0324c62 100644 --- a/litmus-core/pom.xml +++ b/litmus-core/pom.xml @@ -45,6 +45,11 @@ litmus-cache 2.0.8-RELEASE + + com.vladmihalcea + hibernate-types-60 + 2.21.1 + com.navi.medici diff --git a/litmus-core/src/main/java/com/navi/medici/controller/v1/MetricController.java b/litmus-core/src/main/java/com/navi/medici/controller/v1/MetricController.java index d2cba77..199a8f7 100644 --- a/litmus-core/src/main/java/com/navi/medici/controller/v1/MetricController.java +++ b/litmus-core/src/main/java/com/navi/medici/controller/v1/MetricController.java @@ -1,8 +1,14 @@ package com.navi.medici.controller.v1; +import com.navi.medici.request.v1.CreateMetricRequest; +import com.navi.medici.response.MetricResponse; +import com.navi.medici.service.metric.MetricService; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -11,7 +17,11 @@ import org.springframework.web.bind.annotation.RestController; @Log4j2 @RequiredArgsConstructor public class MetricController { + private final MetricService metricService; @PostMapping - public MetricResponse + public ResponseEntity createMetric(@RequestBody CreateMetricRequest createMetricRequest, @RequestHeader("x-session-token") String sessionToken){ + log.info("create request received for creating metric : {}", createMetricRequest.getMetricName()); + return ResponseEntity.ok(metricService.createMetric(createMetricRequest,sessionToken)); + } } diff --git a/litmus-core/src/main/java/com/navi/medici/mapper/MetricMapper.java b/litmus-core/src/main/java/com/navi/medici/mapper/MetricMapper.java new file mode 100644 index 0000000..3edf847 --- /dev/null +++ b/litmus-core/src/main/java/com/navi/medici/mapper/MetricMapper.java @@ -0,0 +1,26 @@ +package com.navi.medici.mapper; + +import com.navi.medici.entity.MetricEntity; +import com.navi.medici.query.experiment.IExperimentQuery; +import com.navi.medici.repository.ExperimentRepository; +import com.navi.medici.response.MetricResponse; +import lombok.AllArgsConstructor; +import lombok.experimental.UtilityClass; +import org.springframework.stereotype.Component; + +@Component +@AllArgsConstructor +public class MetricMapper { + private final ExperimentRepository experimentRepository; + public MetricResponse mapMetricEntityToMetricResponse(MetricEntity metricEntity){ + long numberOfExperimentsForMetrics = experimentRepository.countByPrimaryMetric(metricEntity.getMetricName()); + return MetricResponse.builder() + .metricId(metricEntity.getMetricId()) + .metricOwner(metricEntity.getMetricOwner()) + .metricName(metricEntity.getMetricName()) + .metricType(metricEntity.getMetricType()) + .updatedAt(metricEntity.getUpdatedAt()) + .numberOfExperiments(numberOfExperimentsForMetrics) + .build(); + } +} diff --git a/litmus-core/src/main/java/com/navi/medici/service/metric/MetricService.java b/litmus-core/src/main/java/com/navi/medici/service/metric/MetricService.java new file mode 100644 index 0000000..e4a94fb --- /dev/null +++ b/litmus-core/src/main/java/com/navi/medici/service/metric/MetricService.java @@ -0,0 +1,8 @@ +package com.navi.medici.service.metric; + +import com.navi.medici.request.v1.CreateMetricRequest; +import com.navi.medici.response.MetricResponse; + +public interface MetricService { + MetricResponse createMetric(CreateMetricRequest createMetricRequest, String sessionToken); +} diff --git a/litmus-core/src/main/java/com/navi/medici/service/metric/MetricServiceImpl.java b/litmus-core/src/main/java/com/navi/medici/service/metric/MetricServiceImpl.java new file mode 100644 index 0000000..e1da388 --- /dev/null +++ b/litmus-core/src/main/java/com/navi/medici/service/metric/MetricServiceImpl.java @@ -0,0 +1,38 @@ +package com.navi.medici.service.metric; + +import com.navi.medici.entity.MetricEntity; +import com.navi.medici.mapper.MetricMapper; +import com.navi.medici.repository.MetricRepository; +import com.navi.medici.request.v1.CreateMetricRequest; +import com.navi.medici.request.v1.LitmusOwner; +import com.navi.medici.response.MetricResponse; +import com.navi.medici.validator.MetricRequestValidator; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.UUID; + +@Service +@Getter +@Setter +@AllArgsConstructor +public class MetricServiceImpl implements MetricService { + private final MetricRepository metricRepository; + private final MetricMapper metricMapper; + @Override + @Transactional + public MetricResponse createMetric(CreateMetricRequest createMetricRequest, String sessionToken) { + MetricRequestValidator.validate(createMetricRequest); + MetricEntity metric = MetricEntity.builder() + .metricId(UUID.randomUUID().toString()) + .metricName(createMetricRequest.getMetricName()) + .metricType(createMetricRequest.getMetricType()) + .metricOwner(LitmusOwner.builder().build()) + .athenaQuery(createMetricRequest.getAthenaQuery()) + .build(); + return metricMapper.mapMetricEntityToMetricResponse(metricRepository.save(metric)); + } +} diff --git a/litmus-core/src/main/java/com/navi/medici/validator/MetricRequestValidator.java b/litmus-core/src/main/java/com/navi/medici/validator/MetricRequestValidator.java new file mode 100644 index 0000000..41ef269 --- /dev/null +++ b/litmus-core/src/main/java/com/navi/medici/validator/MetricRequestValidator.java @@ -0,0 +1,21 @@ +package com.navi.medici.validator; + +import com.navi.medici.exceptions.BadRequestException; +import com.navi.medici.request.v1.CreateMetricRequest; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; + +@UtilityClass +public class MetricRequestValidator { + public void validate(CreateMetricRequest createMetricRequest){ + if (Objects.isNull(createMetricRequest)) { + throw new BadRequestException("request for creating metric is null"); + } else if (StringUtils.isBlank(createMetricRequest.getMetricName())) { + throw new BadRequestException("Metric Name cannot be empty"); + } else if (StringUtils.isBlank(createMetricRequest.getAthenaQuery())) { + throw new BadRequestException("Athena Query cannot be empty"); + } + } +} diff --git a/litmus-db/pom.xml b/litmus-db/pom.xml index 8790fb7..2a402b9 100644 --- a/litmus-db/pom.xml +++ b/litmus-db/pom.xml @@ -33,6 +33,12 @@ litmus-model 2.0.8-RELEASE + + com.vladmihalcea + hibernate-types-52 + 2.16.2 + compile + diff --git a/litmus-db/src/main/java/com/navi/medici/entity/ExperimentEntity.java b/litmus-db/src/main/java/com/navi/medici/entity/ExperimentEntity.java index 9a4bb32..696b401 100644 --- a/litmus-db/src/main/java/com/navi/medici/entity/ExperimentEntity.java +++ b/litmus-db/src/main/java/com/navi/medici/entity/ExperimentEntity.java @@ -11,6 +11,9 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Version; + +import com.navi.medici.request.v1.LitmusOwner; +import com.vladmihalcea.hibernate.type.json.JsonBinaryType; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -19,6 +22,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.FieldDefaults; import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.UpdateTimestamp; @Entity @@ -29,6 +34,7 @@ import org.hibernate.annotations.UpdateTimestamp; @NoArgsConstructor @AllArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) +@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) public class ExperimentEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -82,6 +88,22 @@ public class ExperimentEntity { @Column(name = "owner") String experimentOwner; + String primaryMetric; + String secondaryMetric; + + @Type(type = "jsonb") + @Column(columnDefinition = "jsonb") + LitmusOwner experiment_owner; + + long sampleSizeRequired; + + double baselineConversion; + + double minimumDetectableEffect; + + double confidenceInterval; + + long testUsers; } diff --git a/litmus-db/src/main/java/com/navi/medici/entity/MetricEntity.java b/litmus-db/src/main/java/com/navi/medici/entity/MetricEntity.java index cd677f6..21bc717 100644 --- a/litmus-db/src/main/java/com/navi/medici/entity/MetricEntity.java +++ b/litmus-db/src/main/java/com/navi/medici/entity/MetricEntity.java @@ -1,6 +1,8 @@ package com.navi.medici.entity; import com.navi.medici.enums.MetricType; +import com.navi.medici.request.v1.LitmusOwner; +import com.vladmihalcea.hibernate.type.json.JsonBinaryType; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -8,7 +10,10 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.FieldDefaults; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -22,15 +27,19 @@ import javax.persistence.Table; @NoArgsConstructor @AllArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) +@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) public class MetricEntity extends BaseEntity{ - String metricName; - String metricId; + String metricName; @Enumerated(EnumType.STRING) MetricType metricType; - String metricOwner; + + @Type(type = "jsonb") + @Column(columnDefinition = "jsonb") + LitmusOwner metricOwner; + String athenaQuery; } diff --git a/litmus-db/src/main/java/com/navi/medici/repository/ExperimentRepository.java b/litmus-db/src/main/java/com/navi/medici/repository/ExperimentRepository.java index ac615ae..2aedf05 100644 --- a/litmus-db/src/main/java/com/navi/medici/repository/ExperimentRepository.java +++ b/litmus-db/src/main/java/com/navi/medici/repository/ExperimentRepository.java @@ -27,4 +27,6 @@ public interface ExperimentRepository extends CrudRepository findByVertical(String vertical); + + long countByPrimaryMetric(String name); } diff --git a/litmus-liquibase/src/main/resources/db/changelog/202302131535-add-metrics-table.sql b/litmus-liquibase/src/main/resources/db/changelog/202302131535-add-metrics-table.sql index 6b2b0a3..da987b5 100644 --- a/litmus-liquibase/src/main/resources/db/changelog/202302131535-add-metrics-table.sql +++ b/litmus-liquibase/src/main/resources/db/changelog/202302131535-add-metrics-table.sql @@ -6,7 +6,7 @@ CREATE TABLE metrics ( metric_id varchar(36) NOT NULL, metric_name varchar(100) NOT NULL, metric_type varchar(100) NOT NULL, - metric_owner varchar(100) NOT NULL, + metric_owner jsonb, athena_query text NOT NULL, version BIGINT, created_at timestamp, diff --git a/litmus-liquibase/src/main/resources/db/changelog/202302132341-add-enhancement-columns-in-experiment.sql b/litmus-liquibase/src/main/resources/db/changelog/202302132341-add-enhancement-columns-in-experiment.sql new file mode 100644 index 0000000..5b6273f --- /dev/null +++ b/litmus-liquibase/src/main/resources/db/changelog/202302132341-add-enhancement-columns-in-experiment.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql + +--changeset author:akshatsonic id:202302132341 + +ALTER TABLE experiments + ADD primary_metric VARCHAR, + ADD secondary_metric VARCHAR, + ADD sample_size_required BIGINT, + ADD baseline_conversion FLOAT(2), + ADD minimum_detectable_effect FLOAT(2), + ADD confidence_interval FLOAT(2), + ADD test_users BIGINT; \ No newline at end of file diff --git a/litmus-model/src/main/java/com/navi/medici/request/v1/CreateMetricRequest.java b/litmus-model/src/main/java/com/navi/medici/request/v1/CreateMetricRequest.java new file mode 100644 index 0000000..b37a3de --- /dev/null +++ b/litmus-model/src/main/java/com/navi/medici/request/v1/CreateMetricRequest.java @@ -0,0 +1,20 @@ +package com.navi.medici.request.v1; + +import com.navi.medici.enums.MetricType; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class CreateMetricRequest { + String metricName; + String athenaQuery; + MetricType metricType; +} diff --git a/litmus-model/src/main/java/com/navi/medici/request/v1/LitmusOwner.java b/litmus-model/src/main/java/com/navi/medici/request/v1/LitmusOwner.java new file mode 100644 index 0000000..3c5eec8 --- /dev/null +++ b/litmus-model/src/main/java/com/navi/medici/request/v1/LitmusOwner.java @@ -0,0 +1,22 @@ +package com.navi.medici.request.v1; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.FieldDefaults; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +@FieldDefaults(level = AccessLevel.PRIVATE) +public class LitmusOwner { + String emailId; + String name; +} diff --git a/litmus-model/src/main/java/com/navi/medici/response/MetricResponse.java b/litmus-model/src/main/java/com/navi/medici/response/MetricResponse.java new file mode 100644 index 0000000..d8b2c4e --- /dev/null +++ b/litmus-model/src/main/java/com/navi/medici/response/MetricResponse.java @@ -0,0 +1,30 @@ +package com.navi.medici.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.navi.medici.enums.MetricType; +import com.navi.medici.request.v1.LitmusOwner; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.FieldDefaults; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +@FieldDefaults(level = AccessLevel.PRIVATE) +public class MetricResponse { + String metricId; + String metricName; + MetricType metricType; + LitmusOwner metricOwner; + LocalDateTime updatedAt; + Long numberOfExperiments; +}