chore: Updated the usage pulse format (#19565)
## Description The usage pulse format is updated Fixes https://github.com/appsmithorg/cloud-services/issues/148 ## Type of change - Breaking change (fix or feature that would cause existing functionality to not work as expected) ## How Has This Been Tested? - Manual - JUnit ## Checklist: ### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag ### QA activity: - [ ] Test plan has been approved by relevant developers - [ ] Test plan has been peer reviewed by QA - [ ] Cypress test cases have been added and approved by either SDET or manual QA - [ ] Organized project review call with relevant stakeholders after Round 1/2 of QA - [ ] Added Test Plan Approved label after reveiwing all Cypress test
This commit is contained in:
parent
cb39318903
commit
80f23a5af1
|
|
@ -44,6 +44,7 @@ import static com.appsmith.server.constants.Url.PAGE_URL;
|
|||
import static com.appsmith.server.constants.Url.TENANT_URL;
|
||||
import static com.appsmith.server.constants.Url.THEME_URL;
|
||||
import static com.appsmith.server.constants.Url.USER_URL;
|
||||
import static com.appsmith.server.constants.Url.USAGE_PULSE_URL;
|
||||
import static java.time.temporal.ChronoUnit.DAYS;
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
|
|
@ -139,7 +140,8 @@ public class SecurityConfig {
|
|||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, APPLICATION_URL + "/**"),
|
||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, THEME_URL + "/**"),
|
||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, ACTION_URL + "/execute"),
|
||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, TENANT_URL + "/current")
|
||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, TENANT_URL + "/current"),
|
||||
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, USAGE_PULSE_URL)
|
||||
)
|
||||
.permitAll()
|
||||
.pathMatchers("/public/**", "/oauth2/**").permitAll()
|
||||
|
|
|
|||
|
|
@ -168,4 +168,6 @@ public class FieldName {
|
|||
public static final String IS_FORCE_REMOVE = "forceRemove";
|
||||
public static final String UNPUBLISHED_JS_LIBS_IDENTIFIER_IN_APPLICATION_CLASS = "unpublishedCustomJSLibs";
|
||||
public static final String PUBLISHED_JS_LIBS_IDENTIFIER_IN_APPLICATION_CLASS = "publishedCustomJSLibs";
|
||||
public static final String ANONYMOUS_USER_ID = "anonymousUserId";
|
||||
public static final String VIEW_MODE = "viewMode";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
package com.appsmith.server.controllers.ce;
|
||||
|
||||
import com.appsmith.server.dtos.ResponseDTO;
|
||||
import com.appsmith.server.dtos.UsagePulseDTO;
|
||||
import com.appsmith.server.services.UsagePulseService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
|
@ -15,8 +18,8 @@ public class UsagePulseControllerCE {
|
|||
|
||||
@PostMapping
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public Mono<ResponseDTO<Boolean>> create() {
|
||||
return service.createPulse()
|
||||
public Mono<ResponseDTO<Boolean>> create(@RequestBody @Valid UsagePulseDTO usagePulseDTO) {
|
||||
return service.createPulse(usagePulseDTO)
|
||||
.thenReturn(new ResponseDTO<>(HttpStatus.CREATED.value(), true, null));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,16 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
|||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@Document
|
||||
public class UsagePulse extends BaseDomain {
|
||||
|
||||
private String email;
|
||||
|
||||
// Hashed user email
|
||||
private String user;
|
||||
private String instanceId;
|
||||
private String tenantId;
|
||||
private Boolean viewMode;
|
||||
private Boolean isAnonymousUser;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ public class User extends BaseDomain implements UserDetails, OidcUser {
|
|||
|
||||
private String email;
|
||||
|
||||
private String hashedEmail;
|
||||
|
||||
//TODO: This is deprecated in favour of groups
|
||||
private Set<Role> roles;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.appsmith.server.dtos;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class UsagePulseDTO {
|
||||
String anonymousUserId;
|
||||
Boolean viewMode;
|
||||
}
|
||||
|
|
@ -24,6 +24,8 @@ public class UserSessionDTO {
|
|||
|
||||
private String email;
|
||||
|
||||
private String hashedEmail;
|
||||
|
||||
private String name;
|
||||
|
||||
private LoginSource source;
|
||||
|
|
@ -65,6 +67,7 @@ public class UserSessionDTO {
|
|||
|
||||
session.userId = user.getId();
|
||||
session.email = user.getEmail();
|
||||
session.hashedEmail = user.getHashedEmail();
|
||||
session.name = user.getName();
|
||||
session.source = user.getSource();
|
||||
session.state = user.getState();
|
||||
|
|
@ -97,6 +100,7 @@ public class UserSessionDTO {
|
|||
|
||||
user.setId(userId);
|
||||
user.setEmail(email);
|
||||
user.setHashedEmail(hashedEmail);
|
||||
user.setName(name);
|
||||
user.setSource(source);
|
||||
user.setState(state);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@ import org.springframework.stereotype.Service;
|
|||
@Service
|
||||
public class UsagePulseServiceImpl extends UsagePulseServiceCEImpl implements UsagePulseService {
|
||||
|
||||
public UsagePulseServiceImpl(UsagePulseRepository repository, SessionUserService sessionUserService) {
|
||||
super(repository, sessionUserService);
|
||||
public UsagePulseServiceImpl(UsagePulseRepository repository,
|
||||
SessionUserService sessionUserService,
|
||||
UserService userService,
|
||||
TenantService tenantService,
|
||||
ConfigService configService) {
|
||||
super(repository, sessionUserService, userService, tenantService, configService);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package com.appsmith.server.services.ce;
|
||||
|
||||
import com.appsmith.server.domains.UsagePulse;
|
||||
import com.appsmith.server.dtos.UsagePulseDTO;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface UsagePulseServiceCE {
|
||||
Mono<Void> createPulse();
|
||||
Mono<UsagePulse> createPulse(UsagePulseDTO usagePulseDTO);
|
||||
Mono<UsagePulse> save(UsagePulse usagePulse);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,19 @@
|
|||
package com.appsmith.server.services.ce;
|
||||
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.UsagePulse;
|
||||
import com.appsmith.server.domains.User;
|
||||
import com.appsmith.server.dtos.UsagePulseDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import com.appsmith.server.repositories.ce.UsagePulseRepositoryCE;
|
||||
import com.appsmith.server.services.ConfigService;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.services.TenantService;
|
||||
import com.appsmith.server.services.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
|
|
@ -13,11 +23,76 @@ public class UsagePulseServiceCEImpl implements UsagePulseServiceCE {
|
|||
|
||||
private final SessionUserService sessionUserService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final TenantService tenantService;
|
||||
|
||||
private final ConfigService configService;
|
||||
|
||||
/**
|
||||
* To create a usage pulse
|
||||
* @param usagePulseDTO UsagePulseDTO
|
||||
* @return Mono of UsagePulse
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> createPulse() {
|
||||
return sessionUserService.getCurrentUser()
|
||||
.flatMap(user -> repository.save(new UsagePulse(user.getEmail())))
|
||||
.then();
|
||||
public Mono<UsagePulse> createPulse(UsagePulseDTO usagePulseDTO) {
|
||||
if (null == usagePulseDTO.getViewMode()) {
|
||||
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.VIEW_MODE));
|
||||
}
|
||||
|
||||
UsagePulse usagePulse = new UsagePulse();
|
||||
usagePulse.setEmail(null);
|
||||
usagePulse.setViewMode(usagePulseDTO.getViewMode());
|
||||
|
||||
Mono<User> currentUserMono = sessionUserService.getCurrentUser();
|
||||
// TODO: Change to getCurrentTenantId once multi-tenancy in introduced
|
||||
Mono<String> tenantIdMono = tenantService.getDefaultTenantId();
|
||||
Mono<String> instanceIdMono = configService.getInstanceId();
|
||||
|
||||
return Mono.zip(currentUserMono, tenantIdMono, instanceIdMono)
|
||||
.flatMap(tuple -> {
|
||||
User user = tuple.getT1();
|
||||
String tenantId = tuple.getT2();
|
||||
String instanceId = tuple.getT3();
|
||||
usagePulse.setTenantId(tenantId);
|
||||
usagePulse.setInstanceId(instanceId);
|
||||
|
||||
if (user.isAnonymous()) {
|
||||
if (null == usagePulseDTO.getAnonymousUserId()) {
|
||||
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ANONYMOUS_USER_ID));
|
||||
}
|
||||
usagePulse.setIsAnonymousUser(true);
|
||||
usagePulse.setUser(usagePulseDTO.getAnonymousUserId());
|
||||
}
|
||||
else {
|
||||
usagePulse.setIsAnonymousUser(false);
|
||||
if (user.getHashedEmail() == null || StringUtils.isEmpty(user.getHashedEmail())) {
|
||||
String hashedEmail = DigestUtils.sha256Hex(user.getEmail());
|
||||
usagePulse.setUser(hashedEmail);
|
||||
// Hashed user email is stored to user for future mapping of user and pulses
|
||||
User updateUser = new User();
|
||||
updateUser.setHashedEmail(hashedEmail);
|
||||
updateUser.setPasswordResetInitiated(user.getPasswordResetInitiated());
|
||||
updateUser.setSource(user.getSource());
|
||||
updateUser.setGroupIds(null);
|
||||
updateUser.setPolicies(null);
|
||||
|
||||
return Mono.zip(userService.update(user.getId(), updateUser),save(usagePulse))
|
||||
.map(tuple1 -> tuple1.getT2());
|
||||
}
|
||||
usagePulse.setUser(user.getHashedEmail());
|
||||
}
|
||||
return save(usagePulse);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To save usagePulse to the database
|
||||
* @param usagePulse UsagePulse
|
||||
* @return Mono of UsagePulse
|
||||
*/
|
||||
public Mono<UsagePulse> save(UsagePulse usagePulse) {
|
||||
return repository.save(usagePulse);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
package com.appsmith.server.services;
|
||||
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.dtos.UsagePulseDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.security.test.context.support.WithUserDetails;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest
|
||||
@DirtiesContext
|
||||
@Slf4j
|
||||
public class UsagePulseServiceTest {
|
||||
|
||||
@Autowired
|
||||
private UsagePulseService usagePulseService;
|
||||
|
||||
/**
|
||||
* To verify anonymous user usage pulses are logged properly
|
||||
*/
|
||||
@Test
|
||||
@WithUserDetails(value = "anonymousUser")
|
||||
public void test_AnonymousUserPulse_Success() {
|
||||
UsagePulseDTO usagePulseDTO = new UsagePulseDTO();
|
||||
String anonymousUserId = "testAnonymousUserId";
|
||||
usagePulseDTO.setViewMode(false);
|
||||
usagePulseDTO.setAnonymousUserId(anonymousUserId);
|
||||
|
||||
StepVerifier.create(usagePulseService.createPulse(usagePulseDTO))
|
||||
.assertNext(usagePulse -> {
|
||||
assertThat(usagePulse.getId()).isNotNull();
|
||||
assertThat(usagePulse.getEmail()).isNull();
|
||||
assertThat(usagePulse.getUser()).isEqualTo(anonymousUserId);
|
||||
assertThat(usagePulse.getIsAnonymousUser()).isTrue();
|
||||
assertThat(usagePulse.getInstanceId()).isNotNull();
|
||||
assertThat(usagePulse.getTenantId()).isNotNull();
|
||||
assertThat(usagePulse.getViewMode()).isFalse();
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify anonymous usage pulse without anonymousUserId will fail
|
||||
*/
|
||||
@Test
|
||||
@WithUserDetails(value = "anonymousUser")
|
||||
public void test_AnonymousUserPulse_Invalid_AnonymousUserId_ThrowsException() {
|
||||
UsagePulseDTO usagePulseDTO = new UsagePulseDTO();
|
||||
usagePulseDTO.setViewMode(false);
|
||||
|
||||
StepVerifier.create(usagePulseService.createPulse(usagePulseDTO))
|
||||
.expectErrorMessage(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.ANONYMOUS_USER_ID))
|
||||
.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify logged in user usage pulses are logged properly
|
||||
*/
|
||||
@Test
|
||||
@WithUserDetails(value = "api_user")
|
||||
public void test_loggedInUserPulse_Success() {
|
||||
UsagePulseDTO usagePulseDTO = new UsagePulseDTO();
|
||||
usagePulseDTO.setViewMode(true);
|
||||
|
||||
StepVerifier.create(usagePulseService.createPulse(usagePulseDTO))
|
||||
.assertNext(usagePulse -> {
|
||||
String hashedUserEmail = DigestUtils.sha256Hex("api_user");
|
||||
assertThat(usagePulse.getId()).isNotNull();
|
||||
assertThat(usagePulse.getEmail()).isNull();
|
||||
assertThat(usagePulse.getUser()).isEqualTo(hashedUserEmail);
|
||||
assertThat(usagePulse.getIsAnonymousUser()).isFalse();
|
||||
assertThat(usagePulse.getInstanceId()).isNotNull();
|
||||
assertThat(usagePulse.getTenantId()).isNotNull();
|
||||
assertThat(usagePulse.getViewMode()).isTrue();
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* To verify usage pulses without viewMode will fail
|
||||
*/
|
||||
@Test
|
||||
@WithUserDetails(value = "api_user")
|
||||
public void test_Invalid_ViewMode_ThrowsException() {
|
||||
UsagePulseDTO usagePulseDTO = new UsagePulseDTO();
|
||||
|
||||
StepVerifier.create(usagePulseService.createPulse(usagePulseDTO))
|
||||
.expectErrorMessage(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.VIEW_MODE))
|
||||
.verify();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user