Implemented get current user, get user profile, and onboard features
This commit is contained in:
6
pom.xml
6
pom.xml
@@ -87,6 +87,12 @@
|
|||||||
<artifactId>spring-boot-starter-webmvc-test</artifactId>
|
<artifactId>spring-boot-starter-webmvc-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-core</artifactId>
|
||||||
|
<version>5.8.43</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dev.mednikov.social.users.config;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.repositories.IdentifierGenerator;
|
||||||
|
import dev.mednikov.social.users.repositories.IdentifierGeneratorImpl;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class IdentifierGeneratorConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public IdentifierGenerator identifierGenerator() {
|
||||||
|
return new IdentifierGeneratorImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package dev.mednikov.social.users.config;
|
package dev.mednikov.social.users.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -10,6 +12,7 @@ public class ObjectMapperConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
public ObjectMapper objectMapper() {
|
public ObjectMapper objectMapper() {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||||
return objectMapper;
|
return objectMapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package dev.mednikov.social.users.domain;
|
||||||
|
|
||||||
|
public final class OnboardRequestDto {
|
||||||
|
|
||||||
|
private String organizationName;
|
||||||
|
private String description;
|
||||||
|
private boolean student;
|
||||||
|
|
||||||
|
public String getOrganizationName() {
|
||||||
|
return organizationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrganizationName(String organizationName) {
|
||||||
|
this.organizationName = organizationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStudent() {
|
||||||
|
return student;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStudent(boolean student) {
|
||||||
|
this.student = student;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package dev.mednikov.social.users.domain;
|
||||||
|
|
||||||
|
public final class UserProfileDto {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private String email;
|
||||||
|
private boolean active;
|
||||||
|
private boolean onboarded;
|
||||||
|
private boolean emailVerified;
|
||||||
|
private String avatarUrl;
|
||||||
|
private String headline;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnboarded() {
|
||||||
|
return onboarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnboarded(boolean onboarded) {
|
||||||
|
this.onboarded = onboarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmailVerified() {
|
||||||
|
return emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmailVerified(boolean emailVerified) {
|
||||||
|
this.emailVerified = emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatarUrl() {
|
||||||
|
return avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatarUrl(String avatarUrl) {
|
||||||
|
this.avatarUrl = avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeadline() {
|
||||||
|
return headline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeadline(String headline) {
|
||||||
|
this.headline = headline;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package dev.mednikov.social.users.domain;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public final class UserProfileDtoMapper implements Function<User, UserProfileDto> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserProfileDto apply(User user) {
|
||||||
|
UserProfileDto result = new UserProfileDto();
|
||||||
|
result.setId(user.getId().toString());
|
||||||
|
result.setFirstName(user.getFirstName());
|
||||||
|
result.setLastName(user.getLastName());
|
||||||
|
result.setEmail(user.getEmail());
|
||||||
|
result.setHeadline(user.getHeadline());
|
||||||
|
result.setAvatarUrl(user.getAvatarUrl());
|
||||||
|
result.setActive(user.getActive());
|
||||||
|
result.setEmailVerified(user.getEmailVerified());
|
||||||
|
result.setOnboarded(user.getOnboarded());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package dev.mednikov.social.users.events;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
|
public final class UserCreatedEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private final User user;
|
||||||
|
|
||||||
|
public UserCreatedEvent(Object source, User user) {
|
||||||
|
super(source);
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package dev.mednikov.social.users.events;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
|
public final class UserUpdatedEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private final User user;
|
||||||
|
|
||||||
|
public UserUpdatedEvent(Object source, User user) {
|
||||||
|
super(source);
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package dev.mednikov.social.users.exceptions;
|
||||||
|
|
||||||
|
public class UserAlreadyOnboardedException extends ObjectAlreadyExistsException{
|
||||||
|
|
||||||
|
public UserAlreadyOnboardedException(Long userId) {
|
||||||
|
super("User id " + userId.toString() + " is already onboarded");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package dev.mednikov.social.users.messaging;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import dev.mednikov.social.users.events.UserCreatedEvent;
|
||||||
|
import dev.mednikov.social.users.events.UserUpdatedEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageService {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(MessageService.class);
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
private final RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
|
public MessageService(ObjectMapper objectMapper, RabbitTemplate rabbitTemplate) {
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
this.rabbitTemplate = rabbitTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
@Async
|
||||||
|
public void onUserCreatedEventListener (UserCreatedEvent event) {
|
||||||
|
UserPayload payload = new UserPayload(event.getUser());
|
||||||
|
try {
|
||||||
|
String userEncoded = this.objectMapper.writeValueAsString(payload);
|
||||||
|
this.rabbitTemplate.convertAndSend("", "social_users_created", userEncoded);
|
||||||
|
} catch (Exception ex){
|
||||||
|
logger.error(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
@Async
|
||||||
|
public void onUserUpdatedEventListener (UserUpdatedEvent event) {
|
||||||
|
UserPayload payload = new UserPayload(event.getUser());
|
||||||
|
try {
|
||||||
|
String userEncoded = this.objectMapper.writeValueAsString(payload);
|
||||||
|
this.rabbitTemplate.convertAndSend("", "social_users_updated", userEncoded);
|
||||||
|
} catch (Exception ex){
|
||||||
|
logger.error(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package dev.mednikov.social.users.messaging;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
|
||||||
|
final class UserPayload {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private String email;
|
||||||
|
private String avatarUrl;
|
||||||
|
private boolean emailVerified;
|
||||||
|
private boolean active;
|
||||||
|
private boolean onboarded;
|
||||||
|
private String headline;
|
||||||
|
private Long version;
|
||||||
|
|
||||||
|
UserPayload() {}
|
||||||
|
|
||||||
|
UserPayload(User user) {
|
||||||
|
this.id = user.getId().toString();
|
||||||
|
this.firstName = user.getFirstName();
|
||||||
|
this.lastName = user.getLastName();
|
||||||
|
this.email = user.getEmail();
|
||||||
|
this.avatarUrl = user.getAvatarUrl();
|
||||||
|
this.emailVerified = user.getEmailVerified();
|
||||||
|
this.active = user.getActive();
|
||||||
|
this.onboarded = user.getOnboarded();
|
||||||
|
this.headline = user.getHeadline();
|
||||||
|
this.version = user.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAvatarUrl() {
|
||||||
|
return avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEmailVerified() {
|
||||||
|
return emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isOnboarded() {
|
||||||
|
return onboarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getHeadline() {
|
||||||
|
return headline;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatarUrl(String avatarUrl) {
|
||||||
|
this.avatarUrl = avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmailVerified(boolean emailVerified) {
|
||||||
|
this.emailVerified = emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnboarded(boolean onboarded) {
|
||||||
|
this.onboarded = onboarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeadline(String headline) {
|
||||||
|
this.headline = headline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(Long version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
144
src/main/java/dev/mednikov/social/users/models/User.java
Normal file
144
src/main/java/dev/mednikov/social/users/models/User.java
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package dev.mednikov.social.users.models;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
import org.hibernate.annotations.UpdateTimestamp;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "users_user")
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
@Id private Long id;
|
||||||
|
@Column(nullable = false, unique = true, name = "auth_id") private UUID authId;
|
||||||
|
@Column(nullable = false, name = "email", unique = true) private String email;
|
||||||
|
@Column(nullable = false, name = "first_name") private String firstName;
|
||||||
|
@Column(nullable = false, name = "last_name") private String lastName;
|
||||||
|
@Column(nullable = false, name = "avatar_url") private String avatarUrl;
|
||||||
|
@Column(nullable = false, name = "headline") private String headline;
|
||||||
|
@Column(nullable = false, name = "is_active") private Boolean active;
|
||||||
|
@Column(nullable = false, name = "is_email_verified") private Boolean emailVerified;
|
||||||
|
@Column(nullable = false, name = "is_onboarded") private Boolean onboarded;
|
||||||
|
@Column(nullable = false, name = "version") private Long version;
|
||||||
|
@Column(name = "created_at") @CreationTimestamp private LocalDateTime createdAt;
|
||||||
|
@Column(name = "updated_at") @UpdateTimestamp private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean equals(Object o) {
|
||||||
|
if (!(o instanceof User user)) return false;
|
||||||
|
|
||||||
|
return authId.equals(user.authId)
|
||||||
|
&& email.equals(user.email)
|
||||||
|
&& firstName.equals(user.firstName)
|
||||||
|
&& lastName.equals(user.lastName)
|
||||||
|
&& active.equals(user.active)
|
||||||
|
&& onboarded.equals(user.onboarded)
|
||||||
|
&& version.equals(user.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = authId.hashCode();
|
||||||
|
result = 31 * result + email.hashCode();
|
||||||
|
result = 31 * result + firstName.hashCode();
|
||||||
|
result = 31 * result + lastName.hashCode();
|
||||||
|
result = 31 * result + active.hashCode();
|
||||||
|
result = 31 * result + onboarded.hashCode();
|
||||||
|
result = 31 * result + version.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(Long version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getOnboarded() {
|
||||||
|
return onboarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnboarded(Boolean onboarded) {
|
||||||
|
this.onboarded = onboarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEmailVerified() {
|
||||||
|
return emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmailVerified(Boolean emailVerified) {
|
||||||
|
this.emailVerified = emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatarUrl() {
|
||||||
|
return avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatarUrl(String avatarUrl) {
|
||||||
|
this.avatarUrl = avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeadline() {
|
||||||
|
return headline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeadline(String headline) {
|
||||||
|
this.headline = headline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(Boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getAuthId() {
|
||||||
|
return authId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthId(UUID authId) {
|
||||||
|
this.authId = authId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package dev.mednikov.social.users.repositories;
|
||||||
|
|
||||||
|
public interface IdentifierGenerator {
|
||||||
|
|
||||||
|
public Long getNextId();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package dev.mednikov.social.users.repositories;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.generator.SnowflakeGenerator;
|
||||||
|
|
||||||
|
public class IdentifierGeneratorImpl implements IdentifierGenerator {
|
||||||
|
|
||||||
|
private final SnowflakeGenerator snowflakeGenerator;
|
||||||
|
|
||||||
|
public IdentifierGeneratorImpl() {
|
||||||
|
this.snowflakeGenerator = new SnowflakeGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getNextId() {
|
||||||
|
return this.snowflakeGenerator.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.mednikov.social.users.repositories;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
|
||||||
|
Optional<User> findByAuthId (UUID authId);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package dev.mednikov.social.users.services;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
public interface CurrentUserService {
|
||||||
|
|
||||||
|
User getCurrentUser (Jwt authPrincipal);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package dev.mednikov.social.users.services;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.events.UserCreatedEvent;
|
||||||
|
import dev.mednikov.social.users.events.UserUpdatedEvent;
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import dev.mednikov.social.users.repositories.IdentifierGenerator;
|
||||||
|
import dev.mednikov.social.users.repositories.UserRepository;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CurrentUserServiceImpl implements CurrentUserService {
|
||||||
|
|
||||||
|
private final IdentifierGenerator identifierGenerator;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
|
public CurrentUserServiceImpl(IdentifierGenerator identifierGenerator, UserRepository userRepository, ApplicationEventPublisher eventPublisher) {
|
||||||
|
this.identifierGenerator = identifierGenerator;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getCurrentUser(Jwt authPrincipal) {
|
||||||
|
UUID authId = UUID.fromString(authPrincipal.getSubject());
|
||||||
|
Optional<User> userResult = this.userRepository.findByAuthId(authId);
|
||||||
|
if (userResult.isEmpty()) {
|
||||||
|
// Create new user
|
||||||
|
User user = new User();
|
||||||
|
user.setAuthId(authId);
|
||||||
|
user.setFirstName(authPrincipal.getClaim("given_name"));
|
||||||
|
user.setLastName(authPrincipal.getClaim("family_name"));
|
||||||
|
user.setEmailVerified(authPrincipal.getClaim("email_verified"));
|
||||||
|
user.setActive(true);
|
||||||
|
user.setOnboarded(false);
|
||||||
|
user.setId(this.identifierGenerator.getNextId());
|
||||||
|
user.setVersion(1L);
|
||||||
|
user.setAvatarUrl("");
|
||||||
|
user.setHeadline("");
|
||||||
|
|
||||||
|
// Set email
|
||||||
|
String email = authPrincipal.getClaim("email");
|
||||||
|
user.setEmail(email);
|
||||||
|
|
||||||
|
User result = this.userRepository.save(user);
|
||||||
|
UserCreatedEvent event = new UserCreatedEvent(this, result);
|
||||||
|
this.eventPublisher.publishEvent(event);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
User user = userResult.get();
|
||||||
|
// Check if updates are needed
|
||||||
|
if (requiresUpdate(user, authPrincipal)) {
|
||||||
|
// Handle update
|
||||||
|
String email = authPrincipal.getClaim("email");
|
||||||
|
user.setEmail(email);
|
||||||
|
user.setAvatarUrl("");
|
||||||
|
user.setFirstName(authPrincipal.getClaim("given_name"));
|
||||||
|
user.setLastName(authPrincipal.getClaim("family_name"));
|
||||||
|
user.setEmailVerified(authPrincipal.getClaim("email_verified"));
|
||||||
|
Long newVersion = user.getVersion() + 1;
|
||||||
|
user.setVersion(newVersion);
|
||||||
|
User result = this.userRepository.save(user);
|
||||||
|
UserUpdatedEvent event = new UserUpdatedEvent(this, result);
|
||||||
|
this.eventPublisher.publishEvent(event);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean requiresUpdate (User savedUser, Jwt principal){
|
||||||
|
boolean result = !savedUser.getFirstName().equals(principal.getClaim("given_name"));
|
||||||
|
if (!savedUser.getLastName().equals(principal.getClaim("family_name"))) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
if (!savedUser.getEmail().equals(principal.getClaim("email"))) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
if (!savedUser.getEmailVerified().equals(principal.getClaim("email_verified"))) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package dev.mednikov.social.users.services;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.domain.OnboardRequestDto;
|
||||||
|
import dev.mednikov.social.users.domain.UserProfileDto;
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface UserProfileService {
|
||||||
|
|
||||||
|
UserProfileDto onboardCurrentUser (User currentUser, OnboardRequestDto request);
|
||||||
|
|
||||||
|
Optional<UserProfileDto> getUserProfileById (Long id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package dev.mednikov.social.users.services;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.domain.OnboardRequestDto;
|
||||||
|
import dev.mednikov.social.users.domain.UserProfileDto;
|
||||||
|
import dev.mednikov.social.users.domain.UserProfileDtoMapper;
|
||||||
|
import dev.mednikov.social.users.events.UserUpdatedEvent;
|
||||||
|
import dev.mednikov.social.users.exceptions.UserAlreadyOnboardedException;
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import dev.mednikov.social.users.repositories.UserRepository;
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserProfileServiceImpl implements UserProfileService {
|
||||||
|
|
||||||
|
private final static UserProfileDtoMapper mapper = new UserProfileDtoMapper();
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
|
public UserProfileServiceImpl(UserRepository userRepository, ApplicationEventPublisher eventPublisher) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserProfileDto onboardCurrentUser(User currentUser, OnboardRequestDto request) {
|
||||||
|
if (currentUser.getOnboarded()){
|
||||||
|
// User is already onboarded
|
||||||
|
throw new UserAlreadyOnboardedException(currentUser.getId());
|
||||||
|
}
|
||||||
|
if (request.isStudent()){
|
||||||
|
currentUser.setHeadline("Student @ " + request.getOrganizationName());
|
||||||
|
} else {
|
||||||
|
currentUser.setHeadline(request.getDescription() + " @ " + request.getOrganizationName());
|
||||||
|
}
|
||||||
|
currentUser.setOnboarded(true);
|
||||||
|
Long newVersion = currentUser.getVersion() + 1;
|
||||||
|
currentUser.setVersion(newVersion);
|
||||||
|
|
||||||
|
User updatedUser = this.userRepository.save(currentUser);
|
||||||
|
|
||||||
|
UserUpdatedEvent event = new UserUpdatedEvent(this, updatedUser);
|
||||||
|
this.eventPublisher.publishEvent(event);
|
||||||
|
|
||||||
|
return mapper.apply(updatedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<UserProfileDto> getUserProfileById(Long id) {
|
||||||
|
return this.userRepository.findById(id).map(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package dev.mednikov.social.users.web;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.domain.OnboardRequestDto;
|
||||||
|
import dev.mednikov.social.users.domain.UserProfileDto;
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import dev.mednikov.social.users.services.CurrentUserService;
|
||||||
|
import dev.mednikov.social.users.services.UserProfileService;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/profiles")
|
||||||
|
public class UserProfileRestController {
|
||||||
|
|
||||||
|
private final UserProfileService userProfileService;
|
||||||
|
private final CurrentUserService currentUserService;
|
||||||
|
|
||||||
|
public UserProfileRestController(UserProfileService userProfileService, CurrentUserService currentUserService) {
|
||||||
|
this.userProfileService = userProfileService;
|
||||||
|
this.currentUserService = currentUserService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/onboard")
|
||||||
|
public @ResponseBody UserProfileDto onboardCurrentUser (@AuthenticationPrincipal Jwt jwt, @RequestBody OnboardRequestDto body){
|
||||||
|
User currentUser = this.currentUserService.getCurrentUser(jwt);
|
||||||
|
return this.userProfileService.onboardCurrentUser(currentUser, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/user/{userId}")
|
||||||
|
public ResponseEntity<UserProfileDto> getUserProfile (@PathVariable Long userId){
|
||||||
|
Optional<UserProfileDto> result = this.userProfileService.getUserProfileById(userId);
|
||||||
|
return ResponseEntity.of(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package dev.mednikov.social.users.web;
|
||||||
|
|
||||||
|
import dev.mednikov.social.users.domain.UserProfileDto;
|
||||||
|
import dev.mednikov.social.users.domain.UserProfileDtoMapper;
|
||||||
|
import dev.mednikov.social.users.models.User;
|
||||||
|
import dev.mednikov.social.users.services.CurrentUserService;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/users")
|
||||||
|
public class UserRestController {
|
||||||
|
|
||||||
|
private final CurrentUserService currentUserService;
|
||||||
|
|
||||||
|
public UserRestController(CurrentUserService currentUserService) {
|
||||||
|
this.currentUserService = currentUserService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/current")
|
||||||
|
public @ResponseBody UserProfileDto getCurrentUser (@AuthenticationPrincipal Jwt jwt){
|
||||||
|
User user = this.currentUserService.getCurrentUser(jwt);
|
||||||
|
UserProfileDtoMapper mapper = new UserProfileDtoMapper();
|
||||||
|
return mapper.apply(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
15
src/main/resources/db/migration/V1__initial.sql
Normal file
15
src/main/resources/db/migration/V1__initial.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS users_user (
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
auth_id UUID NOT NULL UNIQUE,
|
||||||
|
email VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
first_name VARCHAR(255) NOT NULL,
|
||||||
|
last_name VARCHAR(255) NOT NULL,
|
||||||
|
avatar_url VARCHAR(255) NOT NULL,
|
||||||
|
is_active BOOLEAN NOT NULL,
|
||||||
|
is_onboarded BOOLEAN NOT NULL,
|
||||||
|
is_email_verified BOOLEAN NOT NULL,
|
||||||
|
version BIGINT NOT NULL,
|
||||||
|
headline VARCHAR(255),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user