chore: Add version to analytics events (#20295)
Currently, analytics events don't include Appsmith version information. This PR adds version, and edition information to all events.
This commit is contained in:
parent
ad5d236a05
commit
04f6208f86
|
|
@ -16,6 +16,8 @@ import org.springframework.context.annotation.PropertySource;
|
||||||
@Getter
|
@Getter
|
||||||
public class ProjectProperties {
|
public class ProjectProperties {
|
||||||
|
|
||||||
|
public static final String EDITION = "CE";
|
||||||
|
|
||||||
@Value("${version:UNKNOWN}")
|
@Value("${version:UNKNOWN}")
|
||||||
private String version;
|
private String version;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,7 @@ public interface CustomUserDataRepositoryCE extends AppsmithRepository<UserData>
|
||||||
Mono<UpdateResult> removeIdFromRecentlyUsedList(String userId, String workspaceId, List<String> applicationIds);
|
Mono<UpdateResult> removeIdFromRecentlyUsedList(String userId, String workspaceId, List<String> applicationIds);
|
||||||
|
|
||||||
Flux<UserData> findPhotoAssetsByUserIds(Iterable<String> userId);
|
Flux<UserData> findPhotoAssetsByUserIds(Iterable<String> userId);
|
||||||
|
|
||||||
|
Mono<String> fetchMostRecentlyUsedWorkspaceId(String userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.mongodb.client.result.UpdateResult;
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.data.mongodb.core.query.Update;
|
import org.springframework.data.mongodb.core.query.Update;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
@ -66,4 +67,17 @@ public class CustomUserDataRepositoryCEImpl extends BaseAppsmithRepositoryImpl<U
|
||||||
return queryAll(List.of(criteria), Optional.of(fieldsToInclude), Optional.empty(), Optional.empty());
|
return queryAll(List.of(criteria), Optional.of(fieldsToInclude), Optional.empty(), Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<String> fetchMostRecentlyUsedWorkspaceId(String userId) {
|
||||||
|
final Query query = query(where(fieldName(QUserData.userData.userId)).is(userId));
|
||||||
|
|
||||||
|
query.fields().include(fieldName(QUserData.userData.recentlyUsedWorkspaceIds));
|
||||||
|
|
||||||
|
return mongoOperations.findOne(query, UserData.class)
|
||||||
|
.map(userData -> {
|
||||||
|
final List<String> recentlyUsedWorkspaceIds = userData.getRecentlyUsedWorkspaceIds();
|
||||||
|
return CollectionUtils.isEmpty(recentlyUsedWorkspaceIds) ? "" : recentlyUsedWorkspaceIds.get(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.appsmith.server.services;
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
import com.appsmith.server.configurations.CommonConfig;
|
import com.appsmith.server.configurations.CommonConfig;
|
||||||
|
import com.appsmith.server.configurations.ProjectProperties;
|
||||||
import com.appsmith.server.helpers.PolicyUtils;
|
import com.appsmith.server.helpers.PolicyUtils;
|
||||||
import com.appsmith.server.helpers.UserUtils;
|
import com.appsmith.server.helpers.UserUtils;
|
||||||
|
import com.appsmith.server.repositories.UserDataRepository;
|
||||||
import com.appsmith.server.services.ce.AnalyticsServiceCEImpl;
|
import com.appsmith.server.services.ce.AnalyticsServiceCEImpl;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.segment.analytics.Analytics;
|
import com.segment.analytics.Analytics;
|
||||||
|
|
@ -19,12 +21,10 @@ public class AnalyticsServiceImpl extends AnalyticsServiceCEImpl implements Anal
|
||||||
SessionUserService sessionUserService,
|
SessionUserService sessionUserService,
|
||||||
CommonConfig commonConfig,
|
CommonConfig commonConfig,
|
||||||
ConfigService configService,
|
ConfigService configService,
|
||||||
PolicyUtils policyUtils,
|
|
||||||
UserUtils userUtils,
|
UserUtils userUtils,
|
||||||
Gson gson) {
|
ProjectProperties projectProperties,
|
||||||
|
UserDataRepository userDataRepository) {
|
||||||
super(analytics, sessionUserService, commonConfig, configService, policyUtils, userUtils, gson);
|
super(analytics, sessionUserService, commonConfig, configService, userUtils, projectProperties, userDataRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ public interface AnalyticsServiceCE {
|
||||||
|
|
||||||
Mono<User> identifyUser(User user, UserData userData);
|
Mono<User> identifyUser(User user, UserData userData);
|
||||||
|
|
||||||
|
Mono<User> identifyUser(User user, UserData userData, String recentlyUsedWorkspaceId);
|
||||||
|
|
||||||
void identifyInstance(String instanceId, String role, String useCase);
|
void identifyInstance(String instanceId, String role, String useCase);
|
||||||
|
|
||||||
Mono<Void> sendEvent(String event, String userId, Map<String, ?> properties);
|
Mono<Void> sendEvent(String event, String userId, Map<String, ?> properties);
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@ package com.appsmith.server.services.ce;
|
||||||
import com.appsmith.external.constants.AnalyticsEvents;
|
import com.appsmith.external.constants.AnalyticsEvents;
|
||||||
import com.appsmith.external.models.BaseDomain;
|
import com.appsmith.external.models.BaseDomain;
|
||||||
import com.appsmith.server.configurations.CommonConfig;
|
import com.appsmith.server.configurations.CommonConfig;
|
||||||
|
import com.appsmith.server.configurations.ProjectProperties;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.domains.NewPage;
|
import com.appsmith.server.domains.NewPage;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.domains.UserData;
|
import com.appsmith.server.domains.UserData;
|
||||||
import com.appsmith.server.helpers.ExchangeUtils;
|
import com.appsmith.server.helpers.ExchangeUtils;
|
||||||
import com.appsmith.server.helpers.PolicyUtils;
|
|
||||||
import com.appsmith.server.helpers.UserUtils;
|
import com.appsmith.server.helpers.UserUtils;
|
||||||
|
import com.appsmith.server.repositories.UserDataRepository;
|
||||||
import com.appsmith.server.services.ConfigService;
|
import com.appsmith.server.services.ConfigService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.segment.analytics.Analytics;
|
import com.segment.analytics.Analytics;
|
||||||
import com.segment.analytics.messages.IdentifyMessage;
|
import com.segment.analytics.messages.IdentifyMessage;
|
||||||
import com.segment.analytics.messages.TrackMessage;
|
import com.segment.analytics.messages.TrackMessage;
|
||||||
|
|
@ -39,22 +39,25 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
|
||||||
|
|
||||||
private final UserUtils userUtils;
|
private final UserUtils userUtils;
|
||||||
|
|
||||||
private final Gson gson;
|
private final ProjectProperties projectProperties;
|
||||||
|
|
||||||
|
private final UserDataRepository userDataRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AnalyticsServiceCEImpl(@Autowired(required = false) Analytics analytics,
|
public AnalyticsServiceCEImpl(@Autowired(required = false) Analytics analytics,
|
||||||
SessionUserService sessionUserService,
|
SessionUserService sessionUserService,
|
||||||
CommonConfig commonConfig,
|
CommonConfig commonConfig,
|
||||||
ConfigService configService,
|
ConfigService configService,
|
||||||
PolicyUtils policyUtils,
|
|
||||||
UserUtils userUtils,
|
UserUtils userUtils,
|
||||||
Gson gson) {
|
ProjectProperties projectProperties,
|
||||||
|
UserDataRepository userDataRepository) {
|
||||||
this.analytics = analytics;
|
this.analytics = analytics;
|
||||||
this.sessionUserService = sessionUserService;
|
this.sessionUserService = sessionUserService;
|
||||||
this.commonConfig = commonConfig;
|
this.commonConfig = commonConfig;
|
||||||
this.configService = configService;
|
this.configService = configService;
|
||||||
this.userUtils = userUtils;
|
this.userUtils = userUtils;
|
||||||
this.gson = gson;
|
this.projectProperties = projectProperties;
|
||||||
|
this.userDataRepository = userDataRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
|
|
@ -65,18 +68,34 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
|
||||||
return value == null ? "" : DigestUtils.sha256Hex(value);
|
return value == null ? "" : DigestUtils.sha256Hex(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Mono<User> identifyUser(User user, UserData userData) {
|
public Mono<User> identifyUser(User user, UserData userData) {
|
||||||
|
return identifyUser(user, userData, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<User> identifyUser(User user, UserData userData, String recentlyUsedWorkspaceId) {
|
||||||
if (!isActive()) {
|
if (!isActive()) {
|
||||||
return Mono.just(user);
|
return Mono.just(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mono<Boolean> isSuperUserMono = userUtils.isSuperUser(user);
|
Mono<Boolean> isSuperUserMono = userUtils.isSuperUser(user);
|
||||||
|
|
||||||
return Mono.just(user)
|
final Mono<String> recentlyUsedWorkspaceIdMono = StringUtils.isEmpty(recentlyUsedWorkspaceId)
|
||||||
.zipWith(isSuperUserMono)
|
? userDataRepository.fetchMostRecentlyUsedWorkspaceId(user.getId()).defaultIfEmpty("")
|
||||||
|
: Mono.just(recentlyUsedWorkspaceId);
|
||||||
|
|
||||||
|
return Mono.zip(
|
||||||
|
Mono.just(user),
|
||||||
|
isSuperUserMono,
|
||||||
|
configService.getInstanceId()
|
||||||
|
.defaultIfEmpty("unknown-instance-id"),
|
||||||
|
recentlyUsedWorkspaceIdMono
|
||||||
|
)
|
||||||
.map(tuple -> {
|
.map(tuple -> {
|
||||||
User savedUser = tuple.getT1();
|
final User savedUser = tuple.getT1();
|
||||||
final Boolean isSuperUser = tuple.getT2();
|
final boolean isSuperUser = tuple.getT2();
|
||||||
|
final String instanceId = tuple.getT3();
|
||||||
|
|
||||||
String username = savedUser.getUsername();
|
String username = savedUser.getUsername();
|
||||||
String name = savedUser.getName();
|
String name = savedUser.getName();
|
||||||
|
|
@ -92,7 +111,9 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
|
||||||
.traits(Map.of(
|
.traits(Map.of(
|
||||||
"name", ObjectUtils.defaultIfNull(name, ""),
|
"name", ObjectUtils.defaultIfNull(name, ""),
|
||||||
"email", ObjectUtils.defaultIfNull(email, ""),
|
"email", ObjectUtils.defaultIfNull(email, ""),
|
||||||
"isSuperUser", isSuperUser != null && isSuperUser,
|
"isSuperUser", isSuperUser,
|
||||||
|
"instanceId", instanceId,
|
||||||
|
"mostRecentlyUsedWorkspaceId", tuple.getT4(),
|
||||||
"role", ObjectUtils.defaultIfNull(userData.getRole(), ""),
|
"role", ObjectUtils.defaultIfNull(userData.getRole(), ""),
|
||||||
"goal", ObjectUtils.defaultIfNull(userData.getUseCase(), "")
|
"goal", ObjectUtils.defaultIfNull(userData.getUseCase(), "")
|
||||||
))
|
))
|
||||||
|
|
@ -174,6 +195,7 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
|
||||||
TrackMessage.Builder messageBuilder = TrackMessage.builder(event).userId(userIdToSend);
|
TrackMessage.Builder messageBuilder = TrackMessage.builder(event).userId(userIdToSend);
|
||||||
analyticsProperties.put("originService", "appsmith-server");
|
analyticsProperties.put("originService", "appsmith-server");
|
||||||
analyticsProperties.put("instanceId", instanceId);
|
analyticsProperties.put("instanceId", instanceId);
|
||||||
|
analyticsProperties.put("version", projectProperties.getVersion());
|
||||||
messageBuilder = messageBuilder.properties(analyticsProperties);
|
messageBuilder = messageBuilder.properties(analyticsProperties);
|
||||||
analytics.enqueue(messageBuilder);
|
analytics.enqueue(messageBuilder);
|
||||||
return instanceId;
|
return instanceId;
|
||||||
|
|
@ -226,7 +248,7 @@ public class AnalyticsServiceCEImpl implements AnalyticsServiceCE {
|
||||||
return Mono.just(object);
|
return Mono.just(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String username = (object instanceof User ? (User) object : user).getUsername();
|
final String username = (object instanceof User objectAsUser ? objectAsUser : user).getUsername();
|
||||||
|
|
||||||
HashMap<String, Object> analyticsProperties = new HashMap<>();
|
HashMap<String, Object> analyticsProperties = new HashMap<>();
|
||||||
analyticsProperties.put("id", id);
|
analyticsProperties.put("id", id);
|
||||||
|
|
|
||||||
|
|
@ -50,5 +50,4 @@ public interface UserDataServiceCE {
|
||||||
Mono<UserData> setCommentState(CommentOnboardingState commentOnboardingState);
|
Mono<UserData> setCommentState(CommentOnboardingState commentOnboardingState);
|
||||||
|
|
||||||
Mono<UpdateResult> removeRecentWorkspaceAndApps(String userId, String workspaceId);
|
Mono<UpdateResult> removeRecentWorkspaceAndApps(String userId, String workspaceId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import com.appsmith.server.solutions.ReleaseNotesService;
|
||||||
import com.appsmith.server.solutions.UserChangedHandler;
|
import com.appsmith.server.solutions.UserChangedHandler;
|
||||||
import com.mongodb.DBObject;
|
import com.mongodb.DBObject;
|
||||||
import com.mongodb.client.result.UpdateResult;
|
import com.mongodb.client.result.UpdateResult;
|
||||||
|
import org.apache.commons.collections.map.Flat3Map;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||||
|
|
@ -36,6 +37,8 @@ import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
|
|
||||||
import jakarta.validation.Validator;
|
import jakarta.validation.Validator;
|
||||||
|
import reactor.util.function.Tuple2;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -261,17 +264,25 @@ public class UserDataServiceCEImpl extends BaseService<UserDataRepository, UserD
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Mono<UserData> updateLastUsedAppAndWorkspaceList(Application application) {
|
public Mono<UserData> updateLastUsedAppAndWorkspaceList(Application application) {
|
||||||
return this.getForCurrentUser().flatMap(userData -> {
|
return sessionUserService.getCurrentUser()
|
||||||
// set recently used workspace ids
|
.zipWhen(this::getForUser)
|
||||||
userData.setRecentlyUsedWorkspaceIds(
|
.flatMap(tuple -> {
|
||||||
addIdToRecentList(userData.getRecentlyUsedWorkspaceIds(), application.getWorkspaceId(), 10)
|
final User user = tuple.getT1();
|
||||||
);
|
final UserData userData = tuple.getT2();
|
||||||
// set recently used application ids
|
// set recently used workspace ids
|
||||||
userData.setRecentlyUsedAppIds(
|
userData.setRecentlyUsedWorkspaceIds(
|
||||||
addIdToRecentList(userData.getRecentlyUsedAppIds(), application.getId(), 20)
|
addIdToRecentList(userData.getRecentlyUsedWorkspaceIds(), application.getWorkspaceId(), 10)
|
||||||
);
|
);
|
||||||
return repository.save(userData);
|
// set recently used application ids
|
||||||
});
|
userData.setRecentlyUsedAppIds(
|
||||||
|
addIdToRecentList(userData.getRecentlyUsedAppIds(), application.getId(), 20)
|
||||||
|
);
|
||||||
|
return Mono.zip(
|
||||||
|
analyticsService.identifyUser(user, userData, application.getWorkspaceId()),
|
||||||
|
repository.save(userData)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.map(Tuple2::getT2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -330,4 +341,5 @@ public class UserDataServiceCEImpl extends BaseService<UserDataRepository, UserD
|
||||||
repository.removeIdFromRecentlyUsedList(userId, workspaceId, appIdsList)
|
repository.removeIdFromRecentlyUsedList(userId, workspaceId, appIdsList)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,12 @@ import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.util.UriBuilder;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -64,7 +67,9 @@ public class ReleaseNotesServiceCEImpl implements ReleaseNotesServiceCE {
|
||||||
// isCloudHosted should be true only for our cloud instance,
|
// isCloudHosted should be true only for our cloud instance,
|
||||||
// For docker images that burn the segment key with the image, the CE key will be present
|
// For docker images that burn the segment key with the image, the CE key will be present
|
||||||
"&isSourceInstall=" + (commonConfig.isCloudHosting() || StringUtils.isEmpty(segmentConfig.getCeKey())) +
|
"&isSourceInstall=" + (commonConfig.isCloudHosting() || StringUtils.isEmpty(segmentConfig.getCeKey())) +
|
||||||
(StringUtils.isEmpty(commonConfig.getRepo()) ? "" : ("&repo=" + commonConfig.getRepo()))
|
(StringUtils.isEmpty(commonConfig.getRepo()) ? "" : ("&repo=" + commonConfig.getRepo())) +
|
||||||
|
"&version=" + projectProperties.getVersion() +
|
||||||
|
"&edition=" + ProjectProperties.EDITION
|
||||||
)
|
)
|
||||||
.get()
|
.get()
|
||||||
.exchange()
|
.exchange()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user