From a92e67a6188c5e5d7e3561112c041514100e6cfb Mon Sep 17 00:00:00 2001 From: Shubham Varshney Date: Sun, 21 Jan 2024 19:25:49 +0530 Subject: [PATCH] add distributed id generator --- ...plateServiceJavaSpringBootApplication.java | 6 +- ...JpaApplication.java => SpringBootApp.java} | 5 +- .../commons/contexts/UserIDContextHolder.java | 6 +- .../dao/{entities => }/PersistenceConfig.java | 6 +- .../commons/dao/UserIdAuditorAware.java | 19 ++++ .../commons/dao/address/entities/City.java | 29 ++++++ .../commons/dao/address/entities/Country.java | 23 +++++ .../commons/dao/address/entities/State.java | 26 ++++++ .../address/repositories/CityRepository.java | 10 +++ .../repositories/CountryRepository.java | 10 +++ .../BaseAbstractAuditableEntity.java | 10 +-- .../BaseAbstractDistributedIdEntity.java | 47 ++++++++++ .../entities/BaseAbstractIdEntity.java} | 6 +- .../BaseAbstractIntegerIdEntity.java} | 6 +- .../dao/entities/UserIdAuditorAware.java | 19 ---- .../commons/filters/ContextFilter.java | 4 +- .../commons/generators/id/IDGenerator.java | 7 ++ .../generators/id/constants/Constants.java | 13 +++ .../id/hibernate/DistributedIDGenerator.java | 14 +++ .../id/implementations/NodeIdGenerator.java | 38 ++++++++ .../SnowflakeSequenceIdGenerator.java | 90 +++++++++++++++++++ .../id/implementations}/UUIDGenerator.java | 11 ++- .../code/shubham/commons/models/Event.java | 2 +- .../commons/tree/dao/entities/Tree.java | 4 +- .../code/shubham/commons/utils/UUIDUtils.java | 2 + .../code/shubham/commons/utils/Utils.java | 4 +- .../core/blobstore/dao/entities/Blob.java | 2 +- .../dao/repositories/BlobRepository.java | 4 +- .../core/blobstore/services/BlobService.java | 2 +- .../blobstoremodels/CreateBlobResponse.java | 2 +- .../shubham/core/iam/dao/entities/Role.java | 2 +- .../shubham/core/iam/dao/entities/User.java | 2 +- .../core/iam/dao/entities/UserRole.java | 10 ++- .../iam/dao/repositories/RoleRepository.java | 2 +- .../iam/dao/repositories/UserRepository.java | 2 +- .../dao/repositories/UserRoleRepository.java | 4 +- .../core/iam/services/UserRoleService.java | 4 +- .../core/iam/services/UserService.java | 2 +- .../web/v1/controllers/UserController.java | 2 +- .../shubham/core/iamcommons/IUserService.java | 2 +- .../code/shubham/core/iammodels/UserDTO.java | 2 +- .../code/shubham/core/lock/Constants.java | 2 +- .../lock/{web/v1 => }/dao/entites/Lock.java | 8 +- .../dao/repositories/LockRepository.java | 9 +- .../core/lock/services/LockService.java | 8 +- .../web/v1/controllers/LockController.java | 2 +- .../code/shubham/core/lockmodels/LockDTO.java | 2 + .../userprofile/dao/entities/UserProfile.java | 4 +- .../repositories/UserProfileRepository.java | 4 +- .../services/UserProfileService.java | 9 +- .../v1/controllers/UserProfileController.java | 2 +- .../V1__00001_create_insert_user_role.sql | 30 +++---- .../migration/V2__00002_create_lock_locks.sql | 2 +- .../shubham/commons/TestCommonConstants.java | 2 +- .../UserIdAuditorAwareTest.java | 8 +- .../shubham/commons/dao/address/CityTest.java | 50 +++++++++++ .../commons/dao/address/CountryTest.java | 39 ++++++++ .../UserProfileControllerTest.java | 14 +-- .../shubham/test/AbstractSpringBootTest.java | 5 +- 59 files changed, 534 insertions(+), 127 deletions(-) rename src/main/java/code/shubham/commons/annotations/{SpringBootJpaApplication.java => SpringBootApp.java} (83%) rename src/main/java/code/shubham/commons/dao/{entities => }/PersistenceConfig.java (71%) create mode 100644 src/main/java/code/shubham/commons/dao/UserIdAuditorAware.java create mode 100644 src/main/java/code/shubham/commons/dao/address/entities/City.java create mode 100644 src/main/java/code/shubham/commons/dao/address/entities/Country.java create mode 100644 src/main/java/code/shubham/commons/dao/address/entities/State.java create mode 100644 src/main/java/code/shubham/commons/dao/address/repositories/CityRepository.java create mode 100644 src/main/java/code/shubham/commons/dao/address/repositories/CountryRepository.java rename src/main/java/code/shubham/commons/dao/{entities/base => base/entities}/BaseAbstractAuditableEntity.java (78%) create mode 100644 src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractDistributedIdEntity.java rename src/main/java/code/shubham/commons/dao/{entities/base/BaseIdEntity.java => base/entities/BaseAbstractIdEntity.java} (85%) rename src/main/java/code/shubham/commons/dao/{entities/base/BaseIntegerIdEntity.java => base/entities/BaseAbstractIntegerIdEntity.java} (81%) delete mode 100644 src/main/java/code/shubham/commons/dao/entities/UserIdAuditorAware.java create mode 100644 src/main/java/code/shubham/commons/generators/id/IDGenerator.java create mode 100644 src/main/java/code/shubham/commons/generators/id/constants/Constants.java create mode 100644 src/main/java/code/shubham/commons/generators/id/hibernate/DistributedIDGenerator.java create mode 100644 src/main/java/code/shubham/commons/generators/id/implementations/NodeIdGenerator.java create mode 100644 src/main/java/code/shubham/commons/generators/id/implementations/SnowflakeSequenceIdGenerator.java rename src/main/java/code/shubham/commons/{utils => generators/id/implementations}/UUIDGenerator.java (79%) rename src/main/java/code/shubham/core/lock/{web/v1 => }/dao/entites/Lock.java (69%) rename src/main/java/code/shubham/core/lock/{web/v1 => }/dao/repositories/LockRepository.java (77%) rename src/test/java/code/shubham/commons/dao/{entities => }/UserIdAuditorAwareTest.java (79%) create mode 100644 src/test/java/code/shubham/commons/dao/address/CityTest.java create mode 100644 src/test/java/code/shubham/commons/dao/address/CountryTest.java diff --git a/src/main/java/code/shubham/TemplateServiceJavaSpringBootApplication.java b/src/main/java/code/shubham/TemplateServiceJavaSpringBootApplication.java index 2cf1c1d..09b391a 100644 --- a/src/main/java/code/shubham/TemplateServiceJavaSpringBootApplication.java +++ b/src/main/java/code/shubham/TemplateServiceJavaSpringBootApplication.java @@ -1,11 +1,9 @@ package code.shubham; +import code.shubham.commons.annotations.SpringBootApp; import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; -@ComponentScan("code.shubham") -@SpringBootApplication +@SpringBootApp public class TemplateServiceJavaSpringBootApplication { public static void main(String[] args) { diff --git a/src/main/java/code/shubham/commons/annotations/SpringBootJpaApplication.java b/src/main/java/code/shubham/commons/annotations/SpringBootApp.java similarity index 83% rename from src/main/java/code/shubham/commons/annotations/SpringBootJpaApplication.java rename to src/main/java/code/shubham/commons/annotations/SpringBootApp.java index df4de49..e2a87f5 100644 --- a/src/main/java/code/shubham/commons/annotations/SpringBootJpaApplication.java +++ b/src/main/java/code/shubham/commons/annotations/SpringBootApp.java @@ -11,9 +11,8 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) -@SpringBootApplication @ComponentScan("code.shubham") -@EnableJpaAuditing(auditorAwareRef = "auditorProvider") -public @interface SpringBootJpaApplication { +@SpringBootApplication +public @interface SpringBootApp { } diff --git a/src/main/java/code/shubham/commons/contexts/UserIDContextHolder.java b/src/main/java/code/shubham/commons/contexts/UserIDContextHolder.java index 6251b13..9e1cf5a 100644 --- a/src/main/java/code/shubham/commons/contexts/UserIDContextHolder.java +++ b/src/main/java/code/shubham/commons/contexts/UserIDContextHolder.java @@ -2,13 +2,13 @@ public class UserIDContextHolder { - private static final ThreadLocal CONTEXT = new ThreadLocal<>(); + private static final ThreadLocal CONTEXT = new ThreadLocal<>(); - public static void set(final String id) { + public static void set(final Long id) { CONTEXT.set(id); } - public static String get() { + public static Long get() { return CONTEXT.get(); } diff --git a/src/main/java/code/shubham/commons/dao/entities/PersistenceConfig.java b/src/main/java/code/shubham/commons/dao/PersistenceConfig.java similarity index 71% rename from src/main/java/code/shubham/commons/dao/entities/PersistenceConfig.java rename to src/main/java/code/shubham/commons/dao/PersistenceConfig.java index b76500d..58a1e7f 100644 --- a/src/main/java/code/shubham/commons/dao/entities/PersistenceConfig.java +++ b/src/main/java/code/shubham/commons/dao/PersistenceConfig.java @@ -1,16 +1,16 @@ -package code.shubham.commons.dao.entities; +package code.shubham.commons.dao; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.AuditorAware; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -@EnableJpaAuditing +@EnableJpaAuditing(auditorAwareRef = "auditorProvider") @Configuration public class PersistenceConfig { @Bean - public AuditorAware auditorProvider() { + public AuditorAware auditorProvider() { return new UserIdAuditorAware(); } diff --git a/src/main/java/code/shubham/commons/dao/UserIdAuditorAware.java b/src/main/java/code/shubham/commons/dao/UserIdAuditorAware.java new file mode 100644 index 0000000..f37461d --- /dev/null +++ b/src/main/java/code/shubham/commons/dao/UserIdAuditorAware.java @@ -0,0 +1,19 @@ +package code.shubham.commons.dao; + +import code.shubham.commons.contexts.UserIDContextHolder; +import org.springframework.data.domain.AuditorAware; + +import java.util.Optional; + +public class UserIdAuditorAware implements AuditorAware { + + @Override + public Optional getCurrentAuditor() { + final Optional userId = Optional.ofNullable(UserIDContextHolder.get()); + if (userId.isPresent()) + return userId; + + return Optional.ofNullable(-1L); + } + +} \ No newline at end of file diff --git a/src/main/java/code/shubham/commons/dao/address/entities/City.java b/src/main/java/code/shubham/commons/dao/address/entities/City.java new file mode 100644 index 0000000..749bbbc --- /dev/null +++ b/src/main/java/code/shubham/commons/dao/address/entities/City.java @@ -0,0 +1,29 @@ +package code.shubham.commons.dao.address.entities; + +import code.shubham.commons.dao.base.entities.BaseAbstractDistributedIdEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.*; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = "cities") +public class City extends BaseAbstractDistributedIdEntity { + + @Column(nullable = false) + private String name; + + @Column + private Long stateId; + + @Column(nullable = false, updatable = false) + private Long countryId; + +} diff --git a/src/main/java/code/shubham/commons/dao/address/entities/Country.java b/src/main/java/code/shubham/commons/dao/address/entities/Country.java new file mode 100644 index 0000000..96a11f8 --- /dev/null +++ b/src/main/java/code/shubham/commons/dao/address/entities/Country.java @@ -0,0 +1,23 @@ +package code.shubham.commons.dao.address.entities; + +import code.shubham.commons.dao.base.entities.BaseAbstractDistributedIdEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.*; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = "countries") +public class Country extends BaseAbstractDistributedIdEntity { + + @Column(nullable = false, unique = true) + private String name; + +} diff --git a/src/main/java/code/shubham/commons/dao/address/entities/State.java b/src/main/java/code/shubham/commons/dao/address/entities/State.java new file mode 100644 index 0000000..26ddefb --- /dev/null +++ b/src/main/java/code/shubham/commons/dao/address/entities/State.java @@ -0,0 +1,26 @@ +package code.shubham.commons.dao.address.entities; + +import code.shubham.commons.dao.base.entities.BaseAbstractDistributedIdEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.*; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = "cities") +public class State extends BaseAbstractDistributedIdEntity { + + @Column(nullable = false, unique = true) + private String name; + + @Column(nullable = false, updatable = false) + private Long countryId; + +} diff --git a/src/main/java/code/shubham/commons/dao/address/repositories/CityRepository.java b/src/main/java/code/shubham/commons/dao/address/repositories/CityRepository.java new file mode 100644 index 0000000..8094c2d --- /dev/null +++ b/src/main/java/code/shubham/commons/dao/address/repositories/CityRepository.java @@ -0,0 +1,10 @@ +package code.shubham.commons.dao.address.repositories; + +import code.shubham.commons.dao.address.entities.City; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CityRepository extends JpaRepository { + +} diff --git a/src/main/java/code/shubham/commons/dao/address/repositories/CountryRepository.java b/src/main/java/code/shubham/commons/dao/address/repositories/CountryRepository.java new file mode 100644 index 0000000..21e147e --- /dev/null +++ b/src/main/java/code/shubham/commons/dao/address/repositories/CountryRepository.java @@ -0,0 +1,10 @@ +package code.shubham.commons.dao.address.repositories; + +import code.shubham.commons.dao.address.entities.Country; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CountryRepository extends JpaRepository { + +} diff --git a/src/main/java/code/shubham/commons/dao/entities/base/BaseAbstractAuditableEntity.java b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractAuditableEntity.java similarity index 78% rename from src/main/java/code/shubham/commons/dao/entities/base/BaseAbstractAuditableEntity.java rename to src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractAuditableEntity.java index 8b8f50c..3cbfe33 100644 --- a/src/main/java/code/shubham/commons/dao/entities/base/BaseAbstractAuditableEntity.java +++ b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractAuditableEntity.java @@ -1,4 +1,4 @@ -package code.shubham.commons.dao.entities.base; +package code.shubham.commons.dao.base.entities; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -21,7 +21,7 @@ @EqualsAndHashCode(callSuper = true) @EntityListeners(AuditingEntityListener.class) @JsonIgnoreProperties(value = { "createdAt", "updatedAt" }, allowGetters = true) -public abstract class BaseAbstractAuditableEntity extends BaseIdEntity { +public abstract class BaseAbstractAuditableEntity extends BaseAbstractDistributedIdEntity { private static final long serialVersionUID = 8953324502234883513L; @@ -43,13 +43,11 @@ public abstract class BaseAbstractAuditableEntity extends BaseIdEntity { private Integer version = 0; @JsonIgnore - @Column(name = "created_by", updatable = false, columnDefinition = "VARCHAR(36)", length = 36) @CreatedBy - private String createdBy; + private Long createdBy; @JsonIgnore - @Column(name = "updated_by", columnDefinition = "VARCHAR(36)", length = 36) @LastModifiedBy - private String updatedBy; + private Long updatedBy; } diff --git a/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractDistributedIdEntity.java b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractDistributedIdEntity.java new file mode 100644 index 0000000..984661d --- /dev/null +++ b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractDistributedIdEntity.java @@ -0,0 +1,47 @@ +package code.shubham.commons.dao.base.entities; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.GenericGenerator; + +import java.io.Serializable; +import java.util.Objects; + +@SuperBuilder +@Data +@NoArgsConstructor +@AllArgsConstructor +@MappedSuperclass +public abstract class BaseAbstractDistributedIdEntity implements Serializable { + + private static final long serialVersionUID = 8953224502234813513L; + + @JsonIgnore + @Id + @GeneratedValue(generator = "distributed-id") + @GenericGenerator(name = "distributed-id", + strategy = "code.shubham.commons.generators.id.hibernate.DistributedIDGenerator") + private Long id; + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + final BaseAbstractDistributedIdEntity that = (BaseAbstractDistributedIdEntity) o; + return this.id.equals(that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + +} diff --git a/src/main/java/code/shubham/commons/dao/entities/base/BaseIdEntity.java b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractIdEntity.java similarity index 85% rename from src/main/java/code/shubham/commons/dao/entities/base/BaseIdEntity.java rename to src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractIdEntity.java index 8cd8985..1449cc3 100644 --- a/src/main/java/code/shubham/commons/dao/entities/base/BaseIdEntity.java +++ b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractIdEntity.java @@ -1,4 +1,4 @@ -package code.shubham.commons.dao.entities.base; +package code.shubham.commons.dao.base.entities; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.Column; @@ -19,7 +19,7 @@ @NoArgsConstructor @AllArgsConstructor @MappedSuperclass -public abstract class BaseIdEntity implements Serializable { +public abstract class BaseAbstractIdEntity implements Serializable { private static final long serialVersionUID = 8953224502234883513L; @@ -36,7 +36,7 @@ public boolean equals(Object o) { return true; if (o == null || getClass() != o.getClass()) return false; - BaseIdEntity that = (BaseIdEntity) o; + BaseAbstractIdEntity that = (BaseAbstractIdEntity) o; return this.id.equals(that.id); } diff --git a/src/main/java/code/shubham/commons/dao/entities/base/BaseIntegerIdEntity.java b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractIntegerIdEntity.java similarity index 81% rename from src/main/java/code/shubham/commons/dao/entities/base/BaseIntegerIdEntity.java rename to src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractIntegerIdEntity.java index f389d3d..e15c2c4 100644 --- a/src/main/java/code/shubham/commons/dao/entities/base/BaseIntegerIdEntity.java +++ b/src/main/java/code/shubham/commons/dao/base/entities/BaseAbstractIntegerIdEntity.java @@ -1,4 +1,4 @@ -package code.shubham.commons.dao.entities.base; +package code.shubham.commons.dao.base.entities; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.GeneratedValue; @@ -16,7 +16,7 @@ @NoArgsConstructor @AllArgsConstructor @MappedSuperclass -public abstract class BaseIntegerIdEntity implements Serializable { +public abstract class BaseAbstractIntegerIdEntity implements Serializable { private static final long serialVersionUID = 8953224502234883513L; @@ -31,7 +31,7 @@ public boolean equals(Object o) { return true; if (o == null || getClass() != o.getClass()) return false; - final BaseIntegerIdEntity that = (BaseIntegerIdEntity) o; + final BaseAbstractIntegerIdEntity that = (BaseAbstractIntegerIdEntity) o; return this.id.equals(that.id); } diff --git a/src/main/java/code/shubham/commons/dao/entities/UserIdAuditorAware.java b/src/main/java/code/shubham/commons/dao/entities/UserIdAuditorAware.java deleted file mode 100644 index 0c01dc1..0000000 --- a/src/main/java/code/shubham/commons/dao/entities/UserIdAuditorAware.java +++ /dev/null @@ -1,19 +0,0 @@ -package code.shubham.commons.dao.entities; - -import code.shubham.commons.contexts.UserIDContextHolder; -import org.springframework.data.domain.AuditorAware; - -import java.util.Optional; - -public class UserIdAuditorAware implements AuditorAware { - - @Override - public Optional getCurrentAuditor() { - final Optional userId = Optional.ofNullable(UserIDContextHolder.get()); - if (userId.isPresent()) - return userId; - - return Optional.ofNullable("$$NONE$$"); - } - -} \ No newline at end of file diff --git a/src/main/java/code/shubham/commons/filters/ContextFilter.java b/src/main/java/code/shubham/commons/filters/ContextFilter.java index 4b58006..696ebc3 100644 --- a/src/main/java/code/shubham/commons/filters/ContextFilter.java +++ b/src/main/java/code/shubham/commons/filters/ContextFilter.java @@ -34,7 +34,7 @@ public void doFilter(final ServletRequest servletRequest, final ServletResponse final String userId = Optional.ofNullable(request.getHeader("userId")) .orElse((String) request.getAttribute("userId")); if (userId != null) - UserIDContextHolder.set(userId); + UserIDContextHolder.set(Long.valueOf(userId)); final String tenantId = request.getHeader("tenantId"); if (tenantId != null) @@ -43,7 +43,7 @@ public void doFilter(final ServletRequest servletRequest, final ServletResponse final String userEmail = Optional.ofNullable(request.getHeader("userEmail")) .orElse((String) request.getAttribute("userEmail")); if (userEmail != null) - UserContextHolder.set(new UserDTO(userId, userEmail)); + UserContextHolder.set(new UserDTO(Long.valueOf(userId), userEmail)); chain.doFilter(servletRequest, servletResponse); } diff --git a/src/main/java/code/shubham/commons/generators/id/IDGenerator.java b/src/main/java/code/shubham/commons/generators/id/IDGenerator.java new file mode 100644 index 0000000..995c65a --- /dev/null +++ b/src/main/java/code/shubham/commons/generators/id/IDGenerator.java @@ -0,0 +1,7 @@ +package code.shubham.commons.generators.id; + +public interface IDGenerator { + + Type generate(); + +} \ No newline at end of file diff --git a/src/main/java/code/shubham/commons/generators/id/constants/Constants.java b/src/main/java/code/shubham/commons/generators/id/constants/Constants.java new file mode 100644 index 0000000..2b0b002 --- /dev/null +++ b/src/main/java/code/shubham/commons/generators/id/constants/Constants.java @@ -0,0 +1,13 @@ +package code.shubham.commons.generators.id.constants; + +public class Constants { + + public static final long SIGN_BIT_LENGTH = 1l; + + public static final long EPOCH_BIT_LENGTH = 41l; + + public static final long NODE_ID_BIT_LENGTH = 10l; + + public static final long SEQUENCE_BIT_LENGTH = 12l; + +} \ No newline at end of file diff --git a/src/main/java/code/shubham/commons/generators/id/hibernate/DistributedIDGenerator.java b/src/main/java/code/shubham/commons/generators/id/hibernate/DistributedIDGenerator.java new file mode 100644 index 0000000..a31716b --- /dev/null +++ b/src/main/java/code/shubham/commons/generators/id/hibernate/DistributedIDGenerator.java @@ -0,0 +1,14 @@ +package code.shubham.commons.generators.id.hibernate; + +import code.shubham.commons.generators.id.implementations.SnowflakeSequenceIdGenerator; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.id.IdentifierGenerator; + +public class DistributedIDGenerator implements IdentifierGenerator { + + @Override + public Object generate(final SharedSessionContractImplementor sharedSessionContractImplementor, final Object o) { + return SnowflakeSequenceIdGenerator.getInstance().generate(); + } + +} diff --git a/src/main/java/code/shubham/commons/generators/id/implementations/NodeIdGenerator.java b/src/main/java/code/shubham/commons/generators/id/implementations/NodeIdGenerator.java new file mode 100644 index 0000000..8464ff4 --- /dev/null +++ b/src/main/java/code/shubham/commons/generators/id/implementations/NodeIdGenerator.java @@ -0,0 +1,38 @@ +package code.shubham.commons.generators.id.implementations; + +import code.shubham.commons.generators.id.IDGenerator; + +import java.net.NetworkInterface; +import java.net.SocketException; +import java.security.SecureRandom; +import java.util.Enumeration; + +import static code.shubham.commons.generators.id.constants.Constants.NODE_ID_BIT_LENGTH; + +public class NodeIdGenerator implements IDGenerator { + + private static final long maxNodeIdValue = (1L << NODE_ID_BIT_LENGTH) - 1; + + public Long generate() { + long nodeId; + try { + final StringBuilder builder = new StringBuilder(); + final Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + final NetworkInterface networkInterface = networkInterfaces.nextElement(); + final byte[] mac = networkInterface.getHardwareAddress(); + if (mac != null) + for (int i = 0; i < mac.length; ++i) + builder.append(String.format("%02X", mac[i])); + } + nodeId = builder.toString().hashCode(); + } + catch (final SocketException se) { + // in case of exception get a random number limited by max node size + nodeId = (new SecureRandom().nextInt()); + } + nodeId &= maxNodeIdValue; + return nodeId; + } + +} \ No newline at end of file diff --git a/src/main/java/code/shubham/commons/generators/id/implementations/SnowflakeSequenceIdGenerator.java b/src/main/java/code/shubham/commons/generators/id/implementations/SnowflakeSequenceIdGenerator.java new file mode 100644 index 0000000..6d31686 --- /dev/null +++ b/src/main/java/code/shubham/commons/generators/id/implementations/SnowflakeSequenceIdGenerator.java @@ -0,0 +1,90 @@ +package code.shubham.commons.generators.id.implementations; + +import code.shubham.commons.generators.id.IDGenerator; + +import java.time.Instant; + +import static code.shubham.commons.generators.id.constants.Constants.NODE_ID_BIT_LENGTH; +import static code.shubham.commons.generators.id.constants.Constants.SEQUENCE_BIT_LENGTH; + +public class SnowflakeSequenceIdGenerator implements IDGenerator { + + private final Long nodeId; + + private final long maxSequence = -1L ^ (-1L << SEQUENCE_BIT_LENGTH); + + private final long maxNodeIdValue = -1L ^ (-1L << NODE_ID_BIT_LENGTH); + + private final long EPOCH_START = Instant.EPOCH.toEpochMilli(); + + private final long timeStampLeftSift = NODE_ID_BIT_LENGTH + SEQUENCE_BIT_LENGTH; + + private volatile long currentSequence = 0L; + + private volatile long lastTimestamp = -1L; + + public static SnowflakeSequenceIdGenerator getInstance() { + return SingletonHolder.INSTANCE; + } + + private static final class SingletonHolder { + + public static final SnowflakeSequenceIdGenerator INSTANCE = new SnowflakeSequenceIdGenerator( + new NodeIdGenerator().generate()); + + } + + public SnowflakeSequenceIdGenerator(final long nodeId) { + this.isNodeIdValidOrThrowException(nodeId); + this.nodeId = nodeId; + } + + private void isNodeIdValidOrThrowException(final long nodeId) { + if (nodeId > this.maxNodeIdValue || nodeId < 0) { + throw new IllegalArgumentException( + String.format("Node Id can't be greater than %d or less than 0", this.maxNodeIdValue)); + } + } + + @Override + public Long generate() { + synchronized (this) { + long currentTimeStamp = getTimeStamp(); + if (currentTimeStamp < this.lastTimestamp) + throw new RuntimeException( + String.format("Clock moved backwards. Refusing to generate id for %s milliseconds", + (this.lastTimestamp - currentTimeStamp))); + + if (this.lastTimestamp == currentTimeStamp) { + this.currentSequence = (this.currentSequence + 1) & maxSequence; + if (this.currentSequence == 0) { + currentTimeStamp = this.waitTillNextMillis(); + } + } + else { + this.currentSequence = 0; + } + this.lastTimestamp = currentTimeStamp; + return ((currentTimeStamp - EPOCH_START) << timeStampLeftSift) | (this.nodeId << SEQUENCE_BIT_LENGTH) + | this.currentSequence; + } + } + + private long getTimeStamp() { + return System.currentTimeMillis(); + } + + private long waitTillNextMillis() { + var currentTimeStamp = this.getTimeStamp(); + while (currentTimeStamp <= this.lastTimestamp) + currentTimeStamp = this.getTimeStamp(); + + return currentTimeStamp; + } + + public static void main(String[] args) { + SnowflakeSequenceIdGenerator generator = new SnowflakeSequenceIdGenerator(new NodeIdGenerator().generate()); + System.out.println(generator.generate()); + } + +} \ No newline at end of file diff --git a/src/main/java/code/shubham/commons/utils/UUIDGenerator.java b/src/main/java/code/shubham/commons/generators/id/implementations/UUIDGenerator.java similarity index 79% rename from src/main/java/code/shubham/commons/utils/UUIDGenerator.java rename to src/main/java/code/shubham/commons/generators/id/implementations/UUIDGenerator.java index 10c8de8..9888587 100644 --- a/src/main/java/code/shubham/commons/utils/UUIDGenerator.java +++ b/src/main/java/code/shubham/commons/generators/id/implementations/UUIDGenerator.java @@ -1,11 +1,18 @@ -package code.shubham.commons.utils; +package code.shubham.commons.generators.id.implementations; + +import code.shubham.commons.generators.id.IDGenerator; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.UUID; -public class UUIDGenerator { +public class UUIDGenerator implements IDGenerator { + + @Override + public String generate() { + return UUID.randomUUID().toString(); + } public static UUID generateType5UUID(String name) { try { diff --git a/src/main/java/code/shubham/commons/models/Event.java b/src/main/java/code/shubham/commons/models/Event.java index 32e749e..d0320b6 100644 --- a/src/main/java/code/shubham/commons/models/Event.java +++ b/src/main/java/code/shubham/commons/models/Event.java @@ -17,7 +17,7 @@ public class Event { private String uniqueReferenceId; // idempotency key - private String userId; + private Long userId; private Date createdAt; diff --git a/src/main/java/code/shubham/commons/tree/dao/entities/Tree.java b/src/main/java/code/shubham/commons/tree/dao/entities/Tree.java index e69da39..d140e8b 100644 --- a/src/main/java/code/shubham/commons/tree/dao/entities/Tree.java +++ b/src/main/java/code/shubham/commons/tree/dao/entities/Tree.java @@ -1,6 +1,6 @@ package code.shubham.commons.tree.dao.entities; -import code.shubham.commons.dao.entities.base.BaseIntegerIdEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractIntegerIdEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Index; @@ -16,7 +16,7 @@ @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "trees", indexes = { @Index(name = "index_trees_parentId", columnList = "parentId") }) -public class Tree extends BaseIntegerIdEntity { +public class Tree extends BaseAbstractIntegerIdEntity { private Integer parentId; diff --git a/src/main/java/code/shubham/commons/utils/UUIDUtils.java b/src/main/java/code/shubham/commons/utils/UUIDUtils.java index 26b2615..cc40c90 100644 --- a/src/main/java/code/shubham/commons/utils/UUIDUtils.java +++ b/src/main/java/code/shubham/commons/utils/UUIDUtils.java @@ -1,5 +1,7 @@ package code.shubham.commons.utils; +import code.shubham.commons.generators.id.implementations.UUIDGenerator; + import java.util.UUID; public class UUIDUtils { diff --git a/src/main/java/code/shubham/commons/utils/Utils.java b/src/main/java/code/shubham/commons/utils/Utils.java index a7a0da4..f3fce98 100644 --- a/src/main/java/code/shubham/commons/utils/Utils.java +++ b/src/main/java/code/shubham/commons/utils/Utils.java @@ -21,10 +21,10 @@ public static T getNextInSequence(final List sequence, final T current) { return null; } - public static void validateUserOrThrowException(final String userId) { + public static void validateUserOrThrowException(final Long userId) { if (!userId.equals(UserIDContextHolder.get()) && !RoleContextHolder.isAdmin()) throw new InvalidRequestException("userId", "User with userId: %s not allowed to perform the operation", - userId); + String.valueOf(userId)); } } diff --git a/src/main/java/code/shubham/core/blobstore/dao/entities/Blob.java b/src/main/java/code/shubham/core/blobstore/dao/entities/Blob.java index 6a1eb10..f0221e8 100644 --- a/src/main/java/code/shubham/core/blobstore/dao/entities/Blob.java +++ b/src/main/java/code/shubham/core/blobstore/dao/entities/Blob.java @@ -1,6 +1,6 @@ package code.shubham.core.blobstore.dao.entities; -import code.shubham.commons.dao.entities.base.BaseAbstractAuditableEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractAuditableEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; diff --git a/src/main/java/code/shubham/core/blobstore/dao/repositories/BlobRepository.java b/src/main/java/code/shubham/core/blobstore/dao/repositories/BlobRepository.java index 27a8789..ace4503 100644 --- a/src/main/java/code/shubham/core/blobstore/dao/repositories/BlobRepository.java +++ b/src/main/java/code/shubham/core/blobstore/dao/repositories/BlobRepository.java @@ -7,8 +7,8 @@ import java.util.Optional; @Repository -public interface BlobRepository extends JpaRepository { +public interface BlobRepository extends JpaRepository { - Optional findByIdAndOwner(String id, String owner); + Optional findByIdAndOwner(Long id, String owner); } diff --git a/src/main/java/code/shubham/core/blobstore/services/BlobService.java b/src/main/java/code/shubham/core/blobstore/services/BlobService.java index ef21a86..beeceab 100644 --- a/src/main/java/code/shubham/core/blobstore/services/BlobService.java +++ b/src/main/java/code/shubham/core/blobstore/services/BlobService.java @@ -34,7 +34,7 @@ public CreateBlobResponse getPreSignedUploadUrl(final String owner, final String .build(); } - public Boolean doesBlobExist(final String id, final String owner) { + public Boolean doesBlobExist(final Long id, final String owner) { return this.repository.findByIdAndOwner(id, owner) .map(blob -> true) // S3Utils.doesObjectExist(this.defaultRegion, blob.getBucket(), diff --git a/src/main/java/code/shubham/core/blobstoremodels/CreateBlobResponse.java b/src/main/java/code/shubham/core/blobstoremodels/CreateBlobResponse.java index 64d5677..4f0fef0 100644 --- a/src/main/java/code/shubham/core/blobstoremodels/CreateBlobResponse.java +++ b/src/main/java/code/shubham/core/blobstoremodels/CreateBlobResponse.java @@ -9,7 +9,7 @@ public class CreateBlobResponse { @NotNull - private String blobId; + private Long blobId; @NotNull private String uploadUrl; diff --git a/src/main/java/code/shubham/core/iam/dao/entities/Role.java b/src/main/java/code/shubham/core/iam/dao/entities/Role.java index 56a95a8..419d608 100644 --- a/src/main/java/code/shubham/core/iam/dao/entities/Role.java +++ b/src/main/java/code/shubham/core/iam/dao/entities/Role.java @@ -1,6 +1,6 @@ package code.shubham.core.iam.dao.entities; -import code.shubham.commons.dao.entities.base.BaseAbstractAuditableEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractAuditableEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import lombok.*; diff --git a/src/main/java/code/shubham/core/iam/dao/entities/User.java b/src/main/java/code/shubham/core/iam/dao/entities/User.java index a1aa3df..8fee71a 100644 --- a/src/main/java/code/shubham/core/iam/dao/entities/User.java +++ b/src/main/java/code/shubham/core/iam/dao/entities/User.java @@ -1,6 +1,6 @@ package code.shubham.core.iam.dao.entities; -import code.shubham.commons.dao.entities.base.BaseAbstractAuditableEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractAuditableEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; diff --git a/src/main/java/code/shubham/core/iam/dao/entities/UserRole.java b/src/main/java/code/shubham/core/iam/dao/entities/UserRole.java index c60d7c6..e7a728d 100644 --- a/src/main/java/code/shubham/core/iam/dao/entities/UserRole.java +++ b/src/main/java/code/shubham/core/iam/dao/entities/UserRole.java @@ -1,6 +1,6 @@ package code.shubham.core.iam.dao.entities; -import code.shubham.commons.dao.entities.base.BaseIdEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractIdEntity; import jakarta.persistence.*; import lombok.*; @@ -10,11 +10,13 @@ @AllArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity -@Table(name = "users_roles") -public class UserRole extends BaseIdEntity { +@Table(name = "users_roles", indexes = { @Index(name = "index_users_roles_userId", columnList = "userId") }, + uniqueConstraints = { + @UniqueConstraint(name = "UK_users_roles_userId__role", columnNames = { "userId", "role" }) }) +public class UserRole extends BaseAbstractIdEntity { @Column(nullable = false) - private String userId; + private Long userId; @ManyToOne(optional = false) @JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false) diff --git a/src/main/java/code/shubham/core/iam/dao/repositories/RoleRepository.java b/src/main/java/code/shubham/core/iam/dao/repositories/RoleRepository.java index 47510f3..8ede9f1 100644 --- a/src/main/java/code/shubham/core/iam/dao/repositories/RoleRepository.java +++ b/src/main/java/code/shubham/core/iam/dao/repositories/RoleRepository.java @@ -9,7 +9,7 @@ import java.util.Set; @Repository -public interface RoleRepository extends JpaRepository { +public interface RoleRepository extends JpaRepository { @Query(value = "select * from roles limit 10", nativeQuery = true) Set findAnyTen(); diff --git a/src/main/java/code/shubham/core/iam/dao/repositories/UserRepository.java b/src/main/java/code/shubham/core/iam/dao/repositories/UserRepository.java index a251d5c..dc3449a 100644 --- a/src/main/java/code/shubham/core/iam/dao/repositories/UserRepository.java +++ b/src/main/java/code/shubham/core/iam/dao/repositories/UserRepository.java @@ -8,7 +8,7 @@ import java.util.Optional; @Repository -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { @Query(value = "select * from users where email = ?", nativeQuery = true) Optional findByEmail(String email); diff --git a/src/main/java/code/shubham/core/iam/dao/repositories/UserRoleRepository.java b/src/main/java/code/shubham/core/iam/dao/repositories/UserRoleRepository.java index a63645e..b2ad4ce 100644 --- a/src/main/java/code/shubham/core/iam/dao/repositories/UserRoleRepository.java +++ b/src/main/java/code/shubham/core/iam/dao/repositories/UserRoleRepository.java @@ -7,8 +7,8 @@ import java.util.List; @Repository -public interface UserRoleRepository extends JpaRepository { +public interface UserRoleRepository extends JpaRepository { - List findByUserId(String userId); + List findByUserId(Long userId); } diff --git a/src/main/java/code/shubham/core/iam/services/UserRoleService.java b/src/main/java/code/shubham/core/iam/services/UserRoleService.java index b72cb7e..d41e567 100644 --- a/src/main/java/code/shubham/core/iam/services/UserRoleService.java +++ b/src/main/java/code/shubham/core/iam/services/UserRoleService.java @@ -22,13 +22,13 @@ public UserRoleService(final UserRoleRepository repository, final RoleService ro this.roleService = roleService; } - public UserRole setRoleToUser(final String roleName, final String userId) { + public UserRole setRoleToUser(final String roleName, final Long userId) { return this.roleService.getRoleByName(roleName) .map(role -> this.repository.save(UserRole.builder().role(role).userId(userId).build())) .orElseThrow(() -> new InvalidParameterException("No role with name: %s", roleName)); } - public Collection getAllRoles(String userId) { + public Collection getAllRoles(final Long userId) { return this.repository.findByUserId(userId) .stream() .map(userRole -> userRole.getRole().getName()) diff --git a/src/main/java/code/shubham/core/iam/services/UserService.java b/src/main/java/code/shubham/core/iam/services/UserService.java index cbfa51b..9cc0aab 100644 --- a/src/main/java/code/shubham/core/iam/services/UserService.java +++ b/src/main/java/code/shubham/core/iam/services/UserService.java @@ -48,7 +48,7 @@ public User create(final User user) { } @Override - public GetUserResponse getById(final String userId) { + public GetUserResponse getById(final Long userId) { return this.repository.findById(userId) .map(user -> GetUserResponse.builder() .user(new UserDTO(userId, user.getEmail())) diff --git a/src/main/java/code/shubham/core/iam/web/v1/controllers/UserController.java b/src/main/java/code/shubham/core/iam/web/v1/controllers/UserController.java index 72f5574..1e2db18 100644 --- a/src/main/java/code/shubham/core/iam/web/v1/controllers/UserController.java +++ b/src/main/java/code/shubham/core/iam/web/v1/controllers/UserController.java @@ -31,7 +31,7 @@ public ResponseEntity getOrCreateUser(@RequestBody GetOrCreateUser.Request re } @GetMapping("/{userId}") - public ResponseEntity getById(@PathVariable("userId") final String userId) { + public ResponseEntity getById(@PathVariable("userId") final Long userId) { return ResponseUtils.getDataResponseEntity(HttpStatus.OK, this.userService.getById(userId)); } diff --git a/src/main/java/code/shubham/core/iamcommons/IUserService.java b/src/main/java/code/shubham/core/iamcommons/IUserService.java index f9c0eb6..f373275 100644 --- a/src/main/java/code/shubham/core/iamcommons/IUserService.java +++ b/src/main/java/code/shubham/core/iamcommons/IUserService.java @@ -7,6 +7,6 @@ public interface IUserService { GetUserResponse getOrCreate(GetOrCreateUser.Request request); - GetUserResponse getById(String userId); + GetUserResponse getById(Long userId); } diff --git a/src/main/java/code/shubham/core/iammodels/UserDTO.java b/src/main/java/code/shubham/core/iammodels/UserDTO.java index 494175c..f021ad2 100644 --- a/src/main/java/code/shubham/core/iammodels/UserDTO.java +++ b/src/main/java/code/shubham/core/iammodels/UserDTO.java @@ -1,4 +1,4 @@ package code.shubham.core.iammodels; -public record UserDTO(String id, String email) { +public record UserDTO(Long id, String email) { } diff --git a/src/main/java/code/shubham/core/lock/Constants.java b/src/main/java/code/shubham/core/lock/Constants.java index 578d513..1377bc2 100644 --- a/src/main/java/code/shubham/core/lock/Constants.java +++ b/src/main/java/code/shubham/core/lock/Constants.java @@ -4,7 +4,7 @@ public interface Constants { interface Queries { - String INSERT = "INSERT INTO locks VALUES (UUID(), ?, ?, ?, TIMESTAMPADD(SECOND, ?, NOW()))"; + String INSERT = "INSERT INTO locks VALUES (?, ?, ?, ?, TIMESTAMPADD(SECOND, ?, NOW()))"; String LOCK = "UPDATE locks SET owner = ?, expiry_at = TIMESTAMPADD(SECOND, ?, NOW()), version = version + 1 WHERE name = ? AND version = ? AND (expiry_at is null OR expiry_at < NOW())"; diff --git a/src/main/java/code/shubham/core/lock/web/v1/dao/entites/Lock.java b/src/main/java/code/shubham/core/lock/dao/entites/Lock.java similarity index 69% rename from src/main/java/code/shubham/core/lock/web/v1/dao/entites/Lock.java rename to src/main/java/code/shubham/core/lock/dao/entites/Lock.java index 8a4c0aa..604b813 100644 --- a/src/main/java/code/shubham/core/lock/web/v1/dao/entites/Lock.java +++ b/src/main/java/code/shubham/core/lock/dao/entites/Lock.java @@ -1,11 +1,11 @@ -package code.shubham.core.lock.web.v1.dao.entites; +package code.shubham.core.lock.dao.entites; -import code.shubham.commons.dao.entities.base.BaseIdEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractDistributedIdEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractIdEntity; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import lombok.*; import lombok.experimental.SuperBuilder; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.util.Date; @@ -17,7 +17,7 @@ @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "locks") -public class Lock extends BaseIdEntity { +public class Lock extends BaseAbstractDistributedIdEntity { @Column(nullable = false, unique = true, length = 64) private String name; diff --git a/src/main/java/code/shubham/core/lock/web/v1/dao/repositories/LockRepository.java b/src/main/java/code/shubham/core/lock/dao/repositories/LockRepository.java similarity index 77% rename from src/main/java/code/shubham/core/lock/web/v1/dao/repositories/LockRepository.java rename to src/main/java/code/shubham/core/lock/dao/repositories/LockRepository.java index b8a8909..3bd11da 100644 --- a/src/main/java/code/shubham/core/lock/web/v1/dao/repositories/LockRepository.java +++ b/src/main/java/code/shubham/core/lock/dao/repositories/LockRepository.java @@ -1,23 +1,22 @@ -package code.shubham.core.lock.web.v1.dao.repositories; +package code.shubham.core.lock.dao.repositories; import code.shubham.core.lock.Constants; -import code.shubham.core.lock.web.v1.dao.entites.Lock; +import code.shubham.core.lock.dao.entites.Lock; import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import java.util.Date; import java.util.Optional; @Repository -public interface LockRepository extends JpaRepository { +public interface LockRepository extends JpaRepository { @Transactional @Modifying @Query(nativeQuery = true, value = Constants.Queries.INSERT) - int insert(String name, int version, String owner, long timeToLiveInSeconds); + int insert(Long id, String name, int version, String owner, long timeToLiveInSeconds); Optional findByName(String name); diff --git a/src/main/java/code/shubham/core/lock/services/LockService.java b/src/main/java/code/shubham/core/lock/services/LockService.java index 2884e7d..52a9396 100644 --- a/src/main/java/code/shubham/core/lock/services/LockService.java +++ b/src/main/java/code/shubham/core/lock/services/LockService.java @@ -1,7 +1,8 @@ package code.shubham.core.lock.services; -import code.shubham.core.lock.web.v1.dao.entites.Lock; -import code.shubham.core.lock.web.v1.dao.repositories.LockRepository; +import code.shubham.commons.generators.id.implementations.SnowflakeSequenceIdGenerator; +import code.shubham.core.lock.dao.entites.Lock; +import code.shubham.core.lock.dao.repositories.LockRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -20,7 +21,8 @@ public LockService(final LockRepository repository) { public boolean lock(final String name, final int previousVersion, final String owner, final long timeToLiveInSeconds) { if (previousVersion == 0) - return this.repository.insert(name, 1, owner, timeToLiveInSeconds) == 1; + return this.repository.insert(SnowflakeSequenceIdGenerator.getInstance().generate(), name, 1, owner, + timeToLiveInSeconds) == 1; else return this.repository.lock(owner, timeToLiveInSeconds, name, previousVersion) == 1; } diff --git a/src/main/java/code/shubham/core/lock/web/v1/controllers/LockController.java b/src/main/java/code/shubham/core/lock/web/v1/controllers/LockController.java index 24d0b9e..50c0743 100644 --- a/src/main/java/code/shubham/core/lock/web/v1/controllers/LockController.java +++ b/src/main/java/code/shubham/core/lock/web/v1/controllers/LockController.java @@ -48,7 +48,7 @@ public ResponseEntity getByName(@PathVariable @NotNull @NotEmpty final String return ResponseUtils.getDataResponseEntity(HttpStatus.OK.value(), this.service.fetchByName(name) - .map(lock -> LockDTO.builder().version(lock.getVersion()).build()) + .map(lock -> LockDTO.builder().lockId(lock.getId()).version(lock.getVersion()).build()) .orElseThrow(() -> new InvalidRequestException("name", "no lock for name", name))); } diff --git a/src/main/java/code/shubham/core/lockmodels/LockDTO.java b/src/main/java/code/shubham/core/lockmodels/LockDTO.java index 223295f..8895c2d 100644 --- a/src/main/java/code/shubham/core/lockmodels/LockDTO.java +++ b/src/main/java/code/shubham/core/lockmodels/LockDTO.java @@ -9,6 +9,8 @@ @Data public class LockDTO { + private Long lockId; + @NotNull @NotEmpty private Integer version; diff --git a/src/main/java/code/shubham/core/userprofile/dao/entities/UserProfile.java b/src/main/java/code/shubham/core/userprofile/dao/entities/UserProfile.java index 86ffaa1..4e32d9f 100644 --- a/src/main/java/code/shubham/core/userprofile/dao/entities/UserProfile.java +++ b/src/main/java/code/shubham/core/userprofile/dao/entities/UserProfile.java @@ -1,6 +1,6 @@ package code.shubham.core.userprofile.dao.entities; -import code.shubham.commons.dao.entities.base.BaseAbstractAuditableEntity; +import code.shubham.commons.dao.base.entities.BaseAbstractAuditableEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; @@ -16,7 +16,7 @@ public class UserProfile extends BaseAbstractAuditableEntity { @Column(nullable = false, unique = true) - private String userId; + private Long userId; private String address; diff --git a/src/main/java/code/shubham/core/userprofile/dao/repositories/UserProfileRepository.java b/src/main/java/code/shubham/core/userprofile/dao/repositories/UserProfileRepository.java index 2106e34..7c9859a 100644 --- a/src/main/java/code/shubham/core/userprofile/dao/repositories/UserProfileRepository.java +++ b/src/main/java/code/shubham/core/userprofile/dao/repositories/UserProfileRepository.java @@ -7,8 +7,8 @@ import java.util.Optional; @Repository -public interface UserProfileRepository extends JpaRepository { +public interface UserProfileRepository extends JpaRepository { - Optional findByUserId(String userId); + Optional findByUserId(Long userId); } diff --git a/src/main/java/code/shubham/core/userprofile/services/UserProfileService.java b/src/main/java/code/shubham/core/userprofile/services/UserProfileService.java index 48139db..6d70447 100644 --- a/src/main/java/code/shubham/core/userprofile/services/UserProfileService.java +++ b/src/main/java/code/shubham/core/userprofile/services/UserProfileService.java @@ -16,20 +16,21 @@ public UserProfileService(final UserProfileRepository repository) { this.repository = repository; } - public UserProfile update(final String userId, final String address) { + public UserProfile update(final Long userId, final String address) { return this.repository.save(this.repository.findByUserId(userId).map(profile -> { profile.setAddress(address); return profile; }).orElse(UserProfile.builder().userId(userId).address(address).build())); } - public String getAddress(final String userId) { + public String getAddress(final Long userId) { return this.fetchByUserId(userId).getAddress(); } - public UserProfile fetchByUserId(final String userId) { + public UserProfile fetchByUserId(final Long userId) { return this.repository.findByUserId(userId) - .orElseThrow(() -> new InvalidRequestException("userId", "No user found with id: %s", userId)); + .orElseThrow( + () -> new InvalidRequestException("userId", "No user found with id: %s", String.valueOf(userId))); } } diff --git a/src/main/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileController.java b/src/main/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileController.java index ef51fa3..c4c53f7 100644 --- a/src/main/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileController.java +++ b/src/main/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileController.java @@ -42,7 +42,7 @@ public ResponseEntity update(@RequestBody final UpdateUserProfileRequest requ } @GetMapping - public ResponseEntity getByUserId(@RequestParam("userId") final String userId) { + public ResponseEntity getByUserId(@RequestParam("userId") final Long userId) { Utils.validateUserOrThrowException(userId); return ResponseUtils.getDataResponseEntity(HttpStatus.OK, this.service.fetchByUserId(userId)); } diff --git a/src/main/resources/db/migration/V1__00001_create_insert_user_role.sql b/src/main/resources/db/migration/V1__00001_create_insert_user_role.sql index 2be533b..646da83 100644 --- a/src/main/resources/db/migration/V1__00001_create_insert_user_role.sql +++ b/src/main/resources/db/migration/V1__00001_create_insert_user_role.sql @@ -1,41 +1,41 @@ CREATE TABLE `users` ( - `id` VARCHAR(36) NOT NULL, + `id` BIGINT NOT NULL, `created_at` datetime NOT NULL, - `created_by` VARCHAR(36) NOT NULL, + `created_by` BIGINT NOT NULL, `updated_at` datetime NOT NULL, - `updated_by` VARCHAR(36) NOT NULL, + `updated_by` BIGINT NOT NULL, `version` int DEFAULT NULL, `name` varchar(128) NOT NULL, `email` varchar(128) NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `UK_ofx65keruapi6vyqpv6f2or37` (`email`) + UNIQUE KEY `UK_users_email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -INSERT INTO `users`(`id`, `created_at`, `created_by`, `updated_at`, `updated_by`, `version`, `name`, `email`) VALUES(UUID(), NOW(), "$$FLYWAY$$", NOW(), "$$FLYWAY$$", 1, 'test-admin', 'shubhamv.test@gmail.com'); +INSERT INTO `users`(`id`, `created_at`, `created_by`, `updated_at`, `updated_by`, `version`, `name`, `email`) VALUES(7154825306532495360, NOW(), -1, NOW(), -1, 1, 'test-admin', 'shubhamv.test@gmail.com'); CREATE TABLE `roles` ( - `id` VARCHAR(36) NOT NULL, + `id` BIGINT NOT NULL, `created_at` datetime NOT NULL, - `created_by` VARCHAR(36) NOT NULL, + `created_by` BIGINT NOT NULL, `updated_at` datetime NOT NULL, - `updated_by` VARCHAR(36) NOT NULL, + `updated_by` BIGINT NOT NULL, `version` int DEFAULT NULL, `name` varchar(60) NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `UK_ofx66keruapi6vyqpv6f2or37` (`name`) + UNIQUE KEY `UK_roles_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -INSERT INTO `roles`(`id`,`created_at`, `created_by`, `updated_at`, `updated_by`, `version`, `name`) VALUES(UUID(), NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), 1, 'ADMIN'); -INSERT INTO `roles`(`id`,`created_at`, `created_by`, `updated_at`, `updated_by`, `version`, `name`) VALUES(UUID(), NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), 1, 'USER'); +INSERT INTO `roles`(`id`,`created_at`, `created_by`, `updated_at`, `updated_by`, `version`, `name`) VALUES(7154825376292159488, NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), 1, 'ADMIN'); +INSERT INTO `roles`(`id`,`created_at`, `created_by`, `updated_at`, `updated_by`, `version`, `name`) VALUES(7154825673383100416, NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), NOW(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), 1, 'USER'); CREATE TABLE `users_roles` ( - `id` VARCHAR(36) NOT NULL, - `user_id` VARCHAR(36) NOT NULL, - `role_id` VARCHAR(36) NOT NULL, + `id` BIGINT NOT NULL, + `user_id` BIGINT NOT NULL, + `role_id` BIGINT NOT NULL, PRIMARY KEY (`id`), KEY `FKj6m8fwv7oqv74fcehir1a9ffy` (`role_id`), CONSTRAINT `FKj6m8fwv7oqv74fcehir1a9ffy` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -INSERT INTO `users_roles` (`id`,`user_id`, `role_id`) VALUES(UUID(), (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), (SELECT `id` FROM `roles` WHERE `name` = 'ADMIN')); +INSERT INTO `users_roles` (`id`,`user_id`, `role_id`) VALUES(7154825716202749952, (SELECT `id` FROM `users` WHERE `email` = 'shubhamv.test@gmail.com'), (SELECT `id` FROM `roles` WHERE `name` = 'ADMIN')); diff --git a/src/main/resources/db/migration/V2__00002_create_lock_locks.sql b/src/main/resources/db/migration/V2__00002_create_lock_locks.sql index 1d50fe4..3396770 100644 --- a/src/main/resources/db/migration/V2__00002_create_lock_locks.sql +++ b/src/main/resources/db/migration/V2__00002_create_lock_locks.sql @@ -1,5 +1,5 @@ CREATE TABLE `locks` ( - `id` INT NOT NULL AUTO_INCREMENT, + `id` BIGINT NOT NULL, `name` VARCHAR(64) NOT NULL, `version` INT NOT NULL DEFAULT 0, `owner` VARCHAR(40), diff --git a/src/test/java/code/shubham/commons/TestCommonConstants.java b/src/test/java/code/shubham/commons/TestCommonConstants.java index b52f942..dcd6012 100644 --- a/src/test/java/code/shubham/commons/TestCommonConstants.java +++ b/src/test/java/code/shubham/commons/TestCommonConstants.java @@ -4,7 +4,7 @@ public interface TestCommonConstants { String LOCALHOST = "http://localhost:8080"; - String USER_ID = "ee4a64f6-b2de-44a5-b378-480f79e98e08"; + Long USER_ID = 67898734L; String USER_EMAIL = "test@test.com"; diff --git a/src/test/java/code/shubham/commons/dao/entities/UserIdAuditorAwareTest.java b/src/test/java/code/shubham/commons/dao/UserIdAuditorAwareTest.java similarity index 79% rename from src/test/java/code/shubham/commons/dao/entities/UserIdAuditorAwareTest.java rename to src/test/java/code/shubham/commons/dao/UserIdAuditorAwareTest.java index 2450e13..6610667 100644 --- a/src/test/java/code/shubham/commons/dao/entities/UserIdAuditorAwareTest.java +++ b/src/test/java/code/shubham/commons/dao/UserIdAuditorAwareTest.java @@ -1,4 +1,4 @@ -package code.shubham.commons.dao.entities; +package code.shubham.commons.dao; import code.shubham.commons.TestCommonConstants; import code.shubham.commons.contexts.UserIDContextHolder; @@ -22,7 +22,7 @@ void getCurrentAuditor_Success() { UserIDContextHolder.set(TestCommonConstants.USER_ID); final UserIdAuditorAware auditorAware = new UserIdAuditorAware(); - final Optional response = auditorAware.getCurrentAuditor(); + final Optional response = auditorAware.getCurrentAuditor(); Assertions.assertTrue(response.isPresent()); Assertions.assertEquals(TestCommonConstants.USER_ID, response.get()); @@ -33,10 +33,10 @@ void getCurrentAuditor_without_userId_in_context() { UserIDContextHolder.set(null); final UserIdAuditorAware auditorAware = new UserIdAuditorAware(); - final Optional response = auditorAware.getCurrentAuditor(); + final Optional response = auditorAware.getCurrentAuditor(); Assertions.assertTrue(response.isPresent()); - Assertions.assertEquals("$$NONE$$", response.get()); + Assertions.assertEquals(-1, response.get()); } } \ No newline at end of file diff --git a/src/test/java/code/shubham/commons/dao/address/CityTest.java b/src/test/java/code/shubham/commons/dao/address/CityTest.java new file mode 100644 index 0000000..8c1295a --- /dev/null +++ b/src/test/java/code/shubham/commons/dao/address/CityTest.java @@ -0,0 +1,50 @@ +package code.shubham.commons.dao.address; + +import code.shubham.commons.dao.address.entities.City; +import code.shubham.commons.dao.address.entities.Country; +import code.shubham.commons.dao.address.repositories.CityRepository; +import code.shubham.commons.dao.address.repositories.CountryRepository; +import code.shubham.test.AbstractSpringBootTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class CityTest extends AbstractSpringBootTest { + + @Autowired + private CountryRepository countryRepository; + + @Autowired + private CityRepository repository; + + @BeforeEach + protected void setUp() { + super.setUp(); + truncate("cities", "countries"); + } + + @AfterEach + public void tearDown() { + truncate("cities", "countries"); + } + + @Test + void getId() { + final Country bharat = this.countryRepository.save(Country.builder().name("Bharat").build()); + + final City bengaluru = this.repository.save(City.builder().name("Bengaluru").countryId(bharat.getId()).build()); + + final City pune = this.repository.save(City.builder().name("Pune").countryId(bharat.getId()).build()); + + assertNotNull(bharat); + assertNotNull(bharat.getId()); + assertNotNull(bengaluru); + assertNotNull(bengaluru.getId()); + assertNotNull(pune); + assertNotNull(pune.getId()); + } + +} \ No newline at end of file diff --git a/src/test/java/code/shubham/commons/dao/address/CountryTest.java b/src/test/java/code/shubham/commons/dao/address/CountryTest.java new file mode 100644 index 0000000..e179681 --- /dev/null +++ b/src/test/java/code/shubham/commons/dao/address/CountryTest.java @@ -0,0 +1,39 @@ +package code.shubham.commons.dao.address; + +import code.shubham.commons.dao.address.entities.Country; +import code.shubham.commons.dao.address.repositories.CountryRepository; +import code.shubham.test.AbstractSpringBootTest; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.jupiter.api.Assertions.*; + +class CountryTest extends AbstractSpringBootTest { + + @Autowired + private CountryRepository repository; + + @BeforeEach + protected void setUp() { + super.setUp(); + truncate("countries"); + } + + @AfterEach + public void tearDown() { + truncate("countries"); + } + + @Test + void getId() { + final Country bharat = this.repository.save(Country.builder().name("Bharat").build()); + + final Country usa = this.repository.save(Country.builder().name("USA").build()); + + assertNotNull(bharat); + assertNotNull(bharat.getId()); + assertNotNull(usa); + assertNotNull(usa.getId()); + } + +} \ No newline at end of file diff --git a/src/test/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileControllerTest.java b/src/test/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileControllerTest.java index 6ef4c51..b4422c8 100644 --- a/src/test/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileControllerTest.java +++ b/src/test/java/code/shubham/core/userprofile/web/v1/controllers/UserProfileControllerTest.java @@ -63,7 +63,7 @@ void update_Success() throws Exception { .content(as(request))) .andExpect(status().is(200)) .andExpect(content().json("{\n" + " \"statusCode\": 200,\n" + " \"data\": {\n" - + " \"userId\": \"" + TestCommonConstants.USER_ID + "\",\n" + + " \"userId\": " + TestCommonConstants.USER_ID + ",\n" + " \"address\": \"address\"\n" + " },\n" + " \"error\": null\n" + "}")); final var profiles = this.repository.findByUserId(TestCommonConstants.USER_ID); @@ -85,7 +85,7 @@ void update_Success_with_existing_profile() throws Exception { .content(as(request))) .andExpect(status().is(200)) .andExpect(content().json("{\n" + " \"statusCode\": 200,\n" + " \"data\": {\n" - + " \"userId\": \"" + TestCommonConstants.USER_ID + "\",\n" + + " \"userId\": " + TestCommonConstants.USER_ID + ",\n" + " \"address\": \"address\"\n" + " },\n" + " \"error\": null\n" + "}")); final var profiles = this.repository.findByUserId(TestCommonConstants.USER_ID); @@ -103,7 +103,7 @@ void getByUserId_without_existing_user() throws Exception { this.mockMvc .perform(MockMvcRequestBuilders.get(this.baseURL) .contentType(MediaType.APPLICATION_JSON) - .param("userId", TestCommonConstants.USER_ID) + .param("userId", TestCommonConstants.USER_ID.toString()) .content(as(request))) .andExpect(status().is(400)) .andExpect(content() @@ -124,12 +124,12 @@ void getByUserId_with_invalid_user() throws Exception { this.mockMvc .perform(MockMvcRequestBuilders.get(this.baseURL) .contentType(MediaType.APPLICATION_JSON) - .param("userId", "INVALID_USER_ID") + .param("userId", "1000897923487") .content(as(request))) .andExpect(status().is(400)) .andExpect(content().json("{\n" + " \"statusCode\": 400,\n" + " \"data\": null,\n" + " \"error\": [\n" + " {\n" + " \"userId\": [\n" - + " \"User with userId: INVALID_USER_ID not allowed to perform the operation\"\n" + + " \"User with userId: 1000897923487 not allowed to perform the operation\"\n" + " ]\n" + " }\n" + " ]\n" + "}")); final var profiles = this.repository.findByUserId(TestCommonConstants.USER_ID); @@ -146,11 +146,11 @@ void getByUserId_Success() throws Exception { this.mockMvc .perform(MockMvcRequestBuilders.get(this.baseURL) .contentType(MediaType.APPLICATION_JSON) - .param("userId", TestCommonConstants.USER_ID) + .param("userId", TestCommonConstants.USER_ID.toString()) .content(as(request))) .andExpect(status().is(200)) .andExpect(content().json("{\n" + " \"statusCode\": 200,\n" + " \"data\": {\n" - + " \"userId\": \"" + TestCommonConstants.USER_ID + "\",\n" + + " \"userId\": " + TestCommonConstants.USER_ID + ",\n" + " \"address\": \"address\"\n" + " },\n" + " \"error\": null\n" + "}")); final var profiles = this.repository.findByUserId(TestCommonConstants.USER_ID); diff --git a/src/test/java/code/shubham/test/AbstractSpringBootTest.java b/src/test/java/code/shubham/test/AbstractSpringBootTest.java index f09d38b..8a3960a 100644 --- a/src/test/java/code/shubham/test/AbstractSpringBootTest.java +++ b/src/test/java/code/shubham/test/AbstractSpringBootTest.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import java.util.Arrays; import java.util.Set; @SpringBootTest(classes = TemplateServiceJavaSpringBootApplication.class) @@ -26,8 +27,8 @@ protected void setUp() { UserIDContextHolder.set(null); } - protected void truncate(final String table) { - this.entityManagerRepository.truncateTable(table); + protected void truncate(final String... tables) { + Arrays.stream(tables).forEach(this.entityManagerRepository::truncateTable); } }