-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cb1c09d
commit e7380c1
Showing
20 changed files
with
371 additions
and
58 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package code.shubham.core.lock; | ||
|
||
public interface Constants { | ||
|
||
interface Queries { | ||
|
||
String INSERT = "INSERT INTO locks VALUES (UUID(), ?, ?, ?, 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())"; | ||
|
||
String RENEW = "UPDATE locks SET expiry_at = TIMESTAMPADD(SECOND, ?, expiry_at), version = version + 1 WHERE name = ? AND owner = ? AND version = ? AND now() < expiry_at"; | ||
|
||
String UNLOCK = "UPDATE locks SET owner = null, expiry_at = now(), version = version + 1 WHERE name = ? AND owner = ? AND version = ?"; | ||
|
||
} | ||
|
||
} |
40 changes: 40 additions & 0 deletions
40
src/main/java/code/shubham/core/lock/services/LockService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
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 org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.Optional; | ||
|
||
@Service | ||
public class LockService { | ||
|
||
private final LockRepository repository; | ||
|
||
@Autowired | ||
public LockService(final LockRepository repository) { | ||
this.repository = 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; | ||
else | ||
return this.repository.lock(owner, timeToLiveInSeconds, name, previousVersion) == 1; | ||
} | ||
|
||
public boolean renew(final String name, final int version, final String owner, final long timeToLiveInSeconds) { | ||
return this.repository.renew(timeToLiveInSeconds, name, owner, version) == 1; | ||
} | ||
|
||
public boolean unlock(final String name, final String owner, final int version) { | ||
return this.repository.unlock(name, owner, version) == 1; | ||
} | ||
|
||
public Optional<Lock> fetchByName(final String name) { | ||
return this.repository.findByName(name); | ||
} | ||
|
||
} |
97 changes: 97 additions & 0 deletions
97
src/main/java/code/shubham/core/lock/web/v1/controllers/LockController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package code.shubham.core.lock.web.v1.controllers; | ||
|
||
import code.shubham.commons.exceptions.InvalidRequestException; | ||
import code.shubham.commons.utils.ResponseUtils; | ||
import code.shubham.commons.utils.StringUtils; | ||
import code.shubham.core.lock.services.LockService; | ||
import code.shubham.core.lock.web.v1.validators.LockRequestValidator; | ||
import code.shubham.core.lock.web.v1.validators.UnlockRequestValidator; | ||
import code.shubham.core.lockmodels.LockDTO; | ||
import code.shubham.core.lockmodels.LockRequestDTO; | ||
import code.shubham.core.lockmodels.UnlockRequestDTO; | ||
import io.swagger.v3.oas.annotations.security.SecurityRequirement; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.constraints.NotEmpty; | ||
import jakarta.validation.constraints.NotNull; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PatchMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PutMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping("/v1/locks") | ||
@SecurityRequirement(name = "BearerAuth") | ||
@Tag(name = "Lock") | ||
public class LockController { | ||
|
||
private final LockService service; | ||
|
||
@Autowired | ||
public LockController(final LockService service) { | ||
this.service = service; | ||
} | ||
|
||
@GetMapping("/{name}") | ||
public ResponseEntity<?> getByName(@PathVariable @NotNull @NotEmpty final String name) { | ||
this.validateNameOrThrowException(name); | ||
|
||
return ResponseUtils.getDataResponseEntity(HttpStatus.OK.value(), | ||
this.service.fetchByName(name) | ||
.map(lock -> LockDTO.builder().version(lock.getVersion()).build()) | ||
.orElseThrow(() -> new InvalidRequestException("name", "no lock for name", name))); | ||
|
||
} | ||
|
||
@PutMapping("/{name}") | ||
public ResponseEntity<?> lock(@PathVariable @NotNull @NotEmpty final String name, | ||
@RequestBody final LockRequestDTO request) { | ||
this.validateNameOrThrowException(name); | ||
new LockRequestValidator().validateOrThrowException(request); | ||
|
||
if (this.service.lock(name, request.getPreviousVersion(), request.getOwner(), request.getTimeToLiveInSeconds())) | ||
return ResponseUtils.getResponseEntity(HttpStatus.OK.value()); | ||
|
||
return ResponseUtils.getErrorResponseEntity(HttpStatus.LOCKED.value(), | ||
String.format("%s already locked", name)); | ||
} | ||
|
||
@PatchMapping("/{name}") | ||
public ResponseEntity<?> renew(@PathVariable @NotNull @NotEmpty final String name, | ||
@RequestBody final LockRequestDTO request) { | ||
this.validateNameOrThrowException(name); | ||
new LockRequestValidator().validateOrThrowException(request); | ||
|
||
if (this.service.renew(name, request.getPreviousVersion(), request.getOwner(), | ||
request.getTimeToLiveInSeconds())) | ||
return ResponseUtils.getResponseEntity(HttpStatus.OK.value()); | ||
return ResponseUtils.getErrorResponseEntity(HttpStatus.NOT_FOUND.value(), | ||
String.format("Lock not found on name: %s for owner: %s with version: %d", name, request.getOwner(), | ||
request.getPreviousVersion())); | ||
} | ||
|
||
@DeleteMapping("/{name}") | ||
public ResponseEntity<?> unlock(@PathVariable @NotNull @NotEmpty final String name, | ||
@RequestBody final UnlockRequestDTO request) { | ||
this.validateNameOrThrowException(name); | ||
new UnlockRequestValidator().validateOrThrowException(request); | ||
|
||
if (this.service.unlock(name, request.getOwner(), request.getVersion())) | ||
return ResponseUtils.getResponseEntity(HttpStatus.OK.value()); | ||
return ResponseUtils.getErrorResponseEntity(HttpStatus.NOT_FOUND.value(), | ||
String.format("Lock not found on name: %s for owner: %s with version: %d", name, request.getOwner(), | ||
request.getVersion())); | ||
} | ||
|
||
private void validateNameOrThrowException(final String name) { | ||
if (StringUtils.isEmpty(name)) | ||
throw new InvalidRequestException("name", "must not be empty", "name"); | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
src/main/java/code/shubham/core/lock/web/v1/dao/entites/Lock.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package code.shubham.core.lock.web.v1.dao.entites; | ||
|
||
import code.shubham.commons.dao.entities.base.BaseIdEntity; | ||
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; | ||
|
||
@SuperBuilder | ||
@Builder | ||
@Data | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@EqualsAndHashCode(callSuper = true) | ||
@Entity | ||
@Table(name = "locks") | ||
public class Lock extends BaseIdEntity { | ||
|
||
@Column(nullable = false, unique = true, length = 64) | ||
private String name; | ||
|
||
@Column(length = 64, nullable = false) | ||
private String owner; | ||
|
||
@JsonIgnore | ||
@Temporal(TemporalType.TIMESTAMP) | ||
@Column(name = "expiry_at") | ||
private Date expiryAt; | ||
|
||
@Builder.Default | ||
@Version | ||
private Integer version = 0; | ||
|
||
} |
39 changes: 39 additions & 0 deletions
39
src/main/java/code/shubham/core/lock/web/v1/dao/repositories/LockRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package code.shubham.core.lock.web.v1.dao.repositories; | ||
|
||
import code.shubham.core.lock.Constants; | ||
import code.shubham.core.lock.web.v1.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<Lock, String> { | ||
|
||
@Transactional | ||
@Modifying | ||
@Query(nativeQuery = true, value = Constants.Queries.INSERT) | ||
int insert(String name, int version, String owner, long timeToLiveInSeconds); | ||
|
||
Optional<Lock> findByName(String name); | ||
|
||
@Transactional | ||
@Modifying | ||
@Query(nativeQuery = true, value = Constants.Queries.LOCK) | ||
int lock(String owner, long timeToLiveInSeconds, String name, int previousVersion); | ||
|
||
@Transactional | ||
@Modifying | ||
@Query(nativeQuery = true, value = Constants.Queries.RENEW) | ||
int renew(long timeToLiveInSeconds, String name, String owner, int version); | ||
|
||
@Transactional | ||
@Modifying | ||
@Query(nativeQuery = true, value = Constants.Queries.UNLOCK) | ||
int unlock(String name, String owner, int version); | ||
|
||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/code/shubham/core/lock/web/v1/validators/LockRequestValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package code.shubham.core.lock.web.v1.validators; | ||
|
||
import code.shubham.commons.utils.StringUtils; | ||
import code.shubham.commons.validators.AbstractRequestValidator; | ||
import code.shubham.commons.validators.IValidator; | ||
import code.shubham.core.lockmodels.LockRequestDTO; | ||
|
||
import java.util.Objects; | ||
|
||
public class LockRequestValidator extends AbstractRequestValidator<LockRequestDTO> { | ||
|
||
@Override | ||
public IValidator<LockRequestDTO> validate(final LockRequestDTO request) { | ||
super.validate(request); | ||
|
||
if (StringUtils.isEmpty(request.getOwner())) | ||
this.putMessage("owner", MUST_NOT_BE_EMPTY, "version"); | ||
|
||
if (Objects.isNull(request.getPreviousVersion())) | ||
this.putMessage("previousVersion", MUST_NOT_BE_EMPTY, "previousVersion"); | ||
|
||
if (Objects.isNull(request.getTimeToLiveInSeconds())) | ||
this.putMessage("timeToLiveInSeconds", MUST_NOT_BE_EMPTY, "timeToLiveInSeconds"); | ||
|
||
return this; | ||
} | ||
|
||
} |
Oops, something went wrong.