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;
+}