Merge branch 'feature/segment' into 'master'

Segment & Rollbar Integration

Closes #39 and #38

See merge request theappsmith/internal-tools-server!27
This commit is contained in:
Trisha Anand 2019-09-25 16:20:51 +00:00
commit 426f9b53c1
30 changed files with 552 additions and 368 deletions

View File

@ -1,7 +1,6 @@
package com.appsmith.external.plugins;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.CommandParams;
import com.appsmith.external.models.Param;
import com.appsmith.external.models.ResourceConfiguration;
import org.pf4j.ExtensionPoint;

View File

@ -1,7 +1,6 @@
package com.external.plugins;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.CommandParams;
import com.appsmith.external.models.Param;
import com.appsmith.external.models.ResourceConfiguration;
import com.appsmith.external.plugins.BasePlugin;
@ -13,7 +12,6 @@ import org.pf4j.PluginWrapper;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

View File

@ -1,20 +1,18 @@
package com.external.plugins;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Unit test for simple App.
*/
public class PostgresPluginTest
{
public class PostgresPluginTest {
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
public void shouldAnswerWithTrue() {
assertTrue(true);
}
}

View File

@ -40,6 +40,7 @@ public class RestApiPlugin extends BasePlugin {
if (requestBody == null) {
requestBody = new JSONObject();
}
//TODO: Substitue variables from params in all parts (actionConfig, resourceConfig etc) via JsonPath: https://github.com/json-path/JsonPath
Map<String, Param> propertyMap = params.stream()
.collect(Collectors.toMap(Param::getKey, param -> param));
@ -54,6 +55,7 @@ public class RestApiPlugin extends BasePlugin {
WebClient webClient = WebClient.builder()
.baseUrl(url)
// TODO: Ideally this should come from action / resource config
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
@ -63,6 +65,7 @@ public class RestApiPlugin extends BasePlugin {
Mono<ClientResponse> responseMono = request.exchange();
return responseMono.flatMapMany(response -> {
log.debug("Got response: {}", response);
// TODO: Refactor for better switch case
List<String> contentTypes = response.headers().header(HttpHeaders.CONTENT_TYPE);
Class clazz = String.class;
if (contentTypes != null && contentTypes.size() > 0) {

View File

@ -1,20 +1,18 @@
package com.external.plugins;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Unit test for simple App.
*/
public class RestApiPluginTest
{
public class RestApiPluginTest {
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
public void shouldAnswerWithTrue() {
assertTrue(true);
}
}

View File

@ -101,7 +101,16 @@
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.segment.analytics.java</groupId>
<artifactId>analytics</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
<!-- Plugin dependencies -->
<dependency>
<groupId>org.pf4j</groupId>
@ -132,6 +141,17 @@
<version>3.2.11.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.segment.analytics.java</groupId>
<artifactId>analytics</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.rollbar</groupId>
<artifactId>rollbar-java</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>

View File

@ -0,0 +1,20 @@
package com.appsmith.server.configurations;
import com.rollbar.notifier.Rollbar;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static com.rollbar.notifier.config.ConfigBuilder.withAccessToken;
@Configuration
public class RollbarConfig {
@Value("${com.rollbar.access-token}")
String rollbarAccessToken;
@Bean
Rollbar rollbarConfiguration() {
return Rollbar.init(withAccessToken(rollbarAccessToken).build());
}
}

View File

@ -0,0 +1,17 @@
package com.appsmith.server.configurations;
import com.segment.analytics.Analytics;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SegmentConfig {
@Value("${segment.writeKey}")
private String writeKey;
@Bean
public Analytics analyticsRunner() {
return Analytics.builder(writeKey).build();
}
}

View File

@ -1,7 +1,7 @@
package com.appsmith.server.controllers;
import com.appsmith.server.domains.User;
import com.appsmith.server.services.UserService;
import com.appsmith.server.services.SessionUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@ -16,10 +16,10 @@ import java.security.Principal;
@RequestMapping("")
public class IndexController {
private final UserService service;
private final SessionUserService service;
@Autowired
public IndexController(UserService service) {
public IndexController(SessionUserService service) {
this.service = service;
}

View File

@ -18,5 +18,4 @@ public class PropertyPane extends BaseDomain {
Map<String, List<WidgetSectionProperty>> config;
String configVersion;
}

View File

@ -1,7 +1,10 @@
package com.appsmith.server.exceptions;
import com.appsmith.server.dtos.ResponseDTO;
import com.rollbar.notifier.Rollbar;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ -16,6 +19,14 @@ import reactor.core.publisher.Mono;
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
private final Analytics analytics;
private final Rollbar rollbar;
@Autowired
public GlobalExceptionHandler(Analytics analytics, Rollbar rollbar) {
this.analytics = analytics;
this.rollbar = rollbar;
}
/**
* This function only catches the AppsmithException type and formats it into ResponseEntity<ErrorDTO> object
@ -31,6 +42,7 @@ public class GlobalExceptionHandler {
public Mono<ResponseDTO<ErrorDTO>> catchAppsmithException(AppsmithException e, ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.resolve(e.getHttpStatus()));
log.error("", e);
rollbar.log(e);
return Mono.just(new ResponseDTO<>(e.getHttpStatus(), new ErrorDTO(e.getAppErrorCode(), e.getMessage())));
}
@ -47,6 +59,7 @@ public class GlobalExceptionHandler {
public Mono<ResponseDTO<ErrorDTO>> catchException(Exception e, ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
log.error("", e);
rollbar.log(e);
return Mono.just(new ResponseDTO<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), new ErrorDTO(AppsmithError.INTERNAL_SERVER_ERROR.getHttpErrorCode(),
AppsmithError.INTERNAL_SERVER_ERROR.getMessage())));
}

View File

@ -6,10 +6,12 @@ import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.PageAction;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.Resource;
import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.ExecuteActionDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.ActionRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginManager;
import org.springframework.beans.factory.annotation.Autowired;
@ -44,8 +46,10 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
ResourceService resourceService,
PluginService pluginService,
PageService pageService,
PluginManager pluginManager) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
PluginManager pluginManager,
Analytics analytics,
SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.repository = repository;
this.resourceService = resourceService;
this.pluginService = pluginService;
@ -63,6 +67,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
return Mono.error(new AppsmithException(AppsmithError.PAGE_ID_NOT_GIVEN));
}
Mono<User> userMono = super.sessionUserService.getCurrentUser();
Mono<Resource> resourceMono = resourceService.findById(action.getResourceId());
Mono<Plugin> pluginMono = resourceMono.flatMap(resource -> pluginService.findById(resource.getPluginId()));
Mono<Page> pageMono = pageService.findById(action.getPageId());
@ -96,7 +101,9 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
return page;
})
.flatMap(pageService::save)
.then(Mono.just(action1));
.then(Mono.just(action1))
//Now publish this event
.flatMap(this::segmentTrackCreate);
});
}
@ -129,7 +136,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
return actionMono.flatMap(action -> resourceMono.zipWith(pluginExecutorMono, (resource, pluginExecutor) ->
{
log.debug("*** About to invoke the plugin**");
// TODO: The CommandParams is being passed as null here. Move it to interfaces.CommandParams
// TODO: The CommandParams is being passed as null here. Move it to interfaces.CommandParams - N/A
return pluginExecutor.execute(resource.getResourceConfiguration(), action.getActionConfiguration(), executeActionDTO.getParams());
}))
.flatMapIterable(Flux::toIterable);

View File

@ -6,6 +6,7 @@ import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.ApplicationRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@ -22,12 +23,12 @@ import javax.validation.Validator;
@Service
public class ApplicationServiceImpl extends BaseService<ApplicationRepository, Application, String> implements ApplicationService {
private final UserService userService;
private final Analytics analytics;
@Autowired
public ApplicationServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ApplicationRepository repository, UserService userService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.userService = userService;
public ApplicationServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ApplicationRepository repository, SessionUserService sessionUserService, Analytics analytics) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.analytics = analytics;
}
@Override
@ -36,7 +37,7 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.NAME));
}
Mono<User> userMono = userService.getCurrentUser();
Mono<User> userMono = super.sessionUserService.getCurrentUser();
return userMono
.map(user -> user.getOrganizationId())
@ -44,12 +45,14 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
application.setOrganizationId(orgId);
return application;
})
.flatMap(repository::save);
.flatMap(repository::save)
//Log the event to Segment
.flatMap(this::segmentTrackCreate);
}
@Override
public Flux<Application> get() {
Mono<User> userMono = userService.getCurrentUser();
Mono<User> userMono = super.sessionUserService.getCurrentUser();
return userMono
.map(user -> user.getOrganizationId())
@ -62,7 +65,7 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
}
Mono<User> userMono = userService.getCurrentUser();
Mono<User> userMono = super.sessionUserService.getCurrentUser();
return userMono
.map(user -> user.getOrganizationId())

View File

@ -2,11 +2,14 @@ package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.BaseDomain;
import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.BaseRepository;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.segment.analytics.Analytics;
import com.segment.analytics.messages.TrackMessage;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
@ -17,6 +20,7 @@ import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
import java.util.HashMap;
import java.util.Map;
public abstract class BaseService<R extends BaseRepository, T extends BaseDomain, ID> implements CrudService<T, ID> {
@ -31,16 +35,22 @@ public abstract class BaseService<R extends BaseRepository, T extends BaseDomain
protected final Validator validator;
protected final Analytics analytics;
protected final SessionUserService sessionUserService;
public BaseService(Scheduler scheduler,
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
R repository) {
R repository, Analytics analytics, SessionUserService sessionUserService) {
this.scheduler = scheduler;
this.validator = validator;
this.mongoConverter = mongoConverter;
this.mongoTemplate = reactiveMongoTemplate;
this.repository = repository;
this.analytics = analytics;
this.sessionUserService = sessionUserService;
}
@Override
@ -78,9 +88,11 @@ public abstract class BaseService<R extends BaseRepository, T extends BaseDomain
@Override
public Mono<T> create(T object) {
Mono<User> userMono = sessionUserService.getCurrentUser();
return Mono.just(object)
.flatMap(this::validateObject)
.flatMap(repository::save);
.flatMap(repository::save)
.flatMap(this::segmentTrackCreate);
}
private DBObject getDbObject(Object o) {
@ -106,4 +118,21 @@ public abstract class BaseService<R extends BaseRepository, T extends BaseDomain
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, constraint.stream().findFirst().get().getPropertyPath()));
});
}
protected Mono<T> segmentTrackCreate(Object savedObject) {
Mono<User> userMono = sessionUserService.getCurrentUser();
return userMono
.map(user -> {
HashMap<String, String> analyticsProperties = new HashMap<>();
analyticsProperties.put("id", ((BaseDomain) savedObject).getId());
analyticsProperties.put("organizationId", user.getOrganizationId());
analytics.enqueue(
TrackMessage.builder("MONGO_DB_CREATE_" + savedObject.getClass())
.userId(user.getId())
.properties(analyticsProperties)
);
return (T) savedObject;
})
.switchIfEmpty(Mono.just((T) savedObject));
}
}

View File

@ -4,9 +4,11 @@ import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.OrganizationSetting;
import com.appsmith.server.domains.Setting;
import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.OrganizationRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@ -33,8 +35,10 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
OrganizationRepository repository,
SettingService settingService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
SettingService settingService,
Analytics analytics,
SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.repository = repository;
this.settingService = settingService;
}
@ -55,13 +59,13 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
if (organization == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORGANIZATION));
}
return Mono.just(organization)
.flatMap(this::validateObject)
//transform the organization data to embed setting object in each object in organizationSetting list.
.flatMap(this::enhanceOrganizationSettingList)
//Call the library function to save the updated organization
.flatMap(repository::save);
.flatMap(repository::save)
.flatMap(this::segmentTrackCreate);
}
private Mono<Organization> enhanceOrganizationSettingList(Organization organization) {

View File

@ -7,6 +7,7 @@ import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.PageRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
@ -24,13 +25,12 @@ import java.util.List;
@Slf4j
public class PageServiceImpl extends BaseService<PageRepository, Page, String> implements PageService {
private final UserService userService;
private final ApplicationService applicationService;
@Autowired
public PageServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, PageRepository repository, UserService userService, ApplicationService applicationService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.userService = userService;
public PageServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, PageRepository repository, ApplicationService applicationService,
Analytics analytics, SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.applicationService = applicationService;
}
@ -43,6 +43,9 @@ public class PageServiceImpl extends BaseService<PageRepository, Page, String> i
} else if (page.getApplicationId() == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.APPLICATIONID));
}
Mono<User> userMono = super.sessionUserService.getCurrentUser();
List<Layout> layoutList = page.getLayouts();
if (layoutList == null) {
layoutList = new ArrayList<>();
@ -51,7 +54,9 @@ public class PageServiceImpl extends BaseService<PageRepository, Page, String> i
layoutList.add(createDefaultLayout());
page.setLayouts(layoutList);
}
return repository.save(page);
return repository
.save(page)
.flatMap(this::segmentTrackCreate);
}
@ -72,7 +77,7 @@ public class PageServiceImpl extends BaseService<PageRepository, Page, String> i
@Override
public Mono<Page> doesPageIdBelongToCurrentUserOrganization(Page page) {
Mono<User> userMono = userService.getCurrentUser();
Mono<User> userMono = super.sessionUserService.getCurrentUser();
final String[] username = {null};
return userMono

View File

@ -10,6 +10,7 @@ import com.appsmith.server.dtos.PluginOrgDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.PluginRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@ -30,7 +31,6 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
private final PluginRepository pluginRepository;
private final ApplicationContext applicationContext;
private final OrganizationService organizationService;
private final UserService userService;
@Autowired
public PluginServiceImpl(Scheduler scheduler,
@ -39,12 +39,13 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
ReactiveMongoTemplate reactiveMongoTemplate,
PluginRepository repository,
ApplicationContext applicationContext,
OrganizationService organizationService, UserService userService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
OrganizationService organizationService,
Analytics analytics,
SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.applicationContext = applicationContext;
pluginRepository = repository;
this.organizationService = organizationService;
this.userService = userService;
}
public OldPluginExecutor getPluginExecutor(PluginType pluginType, String className) {
@ -64,8 +65,12 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "id"));
}
Mono<User> userMono = super.sessionUserService.getCurrentUser();
plugin.setDeleted(false);
return pluginRepository.save(plugin);
return pluginRepository
.save(plugin)
.flatMap(this::segmentTrackCreate);
}
@Override
@ -86,7 +91,7 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
}
//Find the organization using id and plugin id -> This is to find if the organization has the plugin installed
Mono<User> userMono = userService.getCurrentUser();
Mono<User> userMono = super.sessionUserService.getCurrentUser();
Mono<Organization> organizationMono = userMono.flatMap(user ->
organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), pluginDTO.getPluginId()));
@ -107,7 +112,7 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
private Mono<Organization> storeOrganizationPlugin(PluginOrgDTO pluginDTO, OrganizationPluginStatus status) {
//Find the organization using id and plugin id -> This is to find if the organization already has the plugin installed
Mono<User> userMono = userService.getCurrentUser();
Mono<User> userMono = super.sessionUserService.getCurrentUser();
Mono<Organization> organizationMono = userMono.flatMap(user ->
organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), pluginDTO.getPluginId()));

View File

@ -6,6 +6,7 @@ import com.appsmith.server.domains.WidgetSectionProperty;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.PropertyPaneRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@ -15,15 +16,14 @@ import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class PropertyPaneServiceImpl extends BaseService<PropertyPaneRepository, PropertyPane, String> implements PropertyPaneService {
public PropertyPaneServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, PropertyPaneRepository repository) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
public PropertyPaneServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, PropertyPaneRepository repository, Analytics analytics, SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
}
@Override
@ -49,9 +49,6 @@ public class PropertyPaneServiceImpl extends BaseService<PropertyPaneRepository,
}
return repository
.save(propertyPane)
.flatMap(savedPropertyPane -> {
savedPropertyPane.setConfigVersion(savedPropertyPane.getId());
return repository.save(savedPropertyPane);
});
.flatMap(this::segmentTrackCreate);
}
}

View File

@ -6,6 +6,7 @@ import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.ResourceRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@ -23,14 +24,13 @@ public class ResourceServiceImpl extends BaseService<ResourceRepository, Resourc
private final ResourceRepository repository;
private final OrganizationService organizationService;
private final UserService userService;
@Autowired
public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, OrganizationService organizationService, PluginService pluginService, UserService userService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, OrganizationService organizationService, PluginService pluginService, Analytics analytics,
SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.repository = repository;
this.organizationService = organizationService;
this.userService = userService;
}
@Override
@ -41,7 +41,7 @@ public class ResourceServiceImpl extends BaseService<ResourceRepository, Resourc
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
}
Mono<User> userMono = userService.getCurrentUser();
Mono<User> userMono = super.sessionUserService.getCurrentUser();
Mono<Organization> organizationMono = userMono.flatMap(user -> organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), resource.getPluginId()));
@ -55,7 +55,8 @@ public class ResourceServiceImpl extends BaseService<ResourceRepository, Resourc
return organizationMono
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, resource.getPluginId())))
.then(updatedResourceMono)
.flatMap(repository::save);
.flatMap(repository::save)
.flatMap(this::segmentTrackCreate);
}
@Override

View File

@ -0,0 +1,8 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.User;
import reactor.core.publisher.Mono;
public interface SessionUserService {
public Mono<User> getCurrentUser();
}

View File

@ -0,0 +1,41 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.User;
import com.appsmith.server.repositories.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@Slf4j
@Service
public class SessionUserServiceImpl implements SessionUserService {
private final UserRepository repository;
public SessionUserServiceImpl(UserRepository userRepository) {
this.repository = userRepository;
}
@Override
public Mono<User> getCurrentUser() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getPrincipal)
.flatMap(principal -> {
String email;
if (principal instanceof org.springframework.security.core.userdetails.User) {
org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User) principal;
//Assumption that the user has inputted an email as username during user creation and not english passport name
email = user.getUsername();
} else {
DefaultOidcUser defaultOidcUser = (DefaultOidcUser) principal;
email = defaultOidcUser.getEmail();
}
return repository.findByEmail(email);
});
}
}

View File

@ -2,6 +2,7 @@ package com.appsmith.server.services;
import com.appsmith.server.domains.Setting;
import com.appsmith.server.repositories.SettingRepository;
import com.segment.analytics.Analytics;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@ -21,8 +22,10 @@ public class SettingServiceImpl extends BaseService<SettingRepository, Setting,
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
SettingRepository repository) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
SettingRepository repository,
Analytics analytics,
SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.repository = repository;
}

View File

@ -11,5 +11,4 @@ public interface UserService extends CrudService<User, String> {
Mono<User> save(User newUser);
Mono<User> getCurrentUser();
}

View File

@ -5,22 +5,22 @@ import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.BeanCopyUtils;
import com.appsmith.server.repositories.UserRepository;
import com.segment.analytics.Analytics;
import com.segment.analytics.messages.IdentifyMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
@ -28,16 +28,21 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
private UserRepository repository;
private final OrganizationService organizationService;
private final Analytics analytics;
@Autowired
public UserServiceImpl(Scheduler scheduler,
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
UserRepository repository, OrganizationService organizationService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
UserRepository repository,
OrganizationService organizationService,
Analytics analytics,
SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analytics, sessionUserService);
this.repository = repository;
this.organizationService = organizationService;
this.analytics = analytics;
}
@Override
@ -52,7 +57,20 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
@Override
public Mono<User> save(User user) {
return repository.save(user);
Mono<User> savedUserMono = repository.save(user);
return savedUserMono
.map(savedUser -> {
Map<String, String> traitsMap = new HashMap<>();
traitsMap.put("name", savedUser.getName());
traitsMap.put("email", savedUser.getEmail());
analytics.enqueue(IdentifyMessage.builder()
.userId(savedUser.getId())
.traits(traitsMap)
);
analytics.flush();
return savedUser;
});
}
@Override
@ -60,25 +78,6 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
return repository.findByName(username).block();
}
@Override
public Mono<User> getCurrentUser() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getPrincipal)
.flatMap(principal -> {
String email;
if (principal instanceof org.springframework.security.core.userdetails.User) {
org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User) principal;
//Assumption that the user has inputted an email as username during user creation and not english passport name
email = user.getUsername();
} else {
DefaultOidcUser defaultOidcUser = (DefaultOidcUser) principal;
email = defaultOidcUser.getEmail();
}
return repository.findByEmail(email);
});
}
@Override
public Mono<User> update(String id, User userUpdate) {
Mono<User> userFromRepository = repository.findById(id);

View File

@ -2,6 +2,7 @@ package com.appsmith.server.services;
import com.appsmith.server.domains.Widget;
import com.appsmith.server.repositories.WidgetRepository;
import com.segment.analytics.Analytics;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@ -23,8 +24,10 @@ public class WidgetServiceImpl extends BaseService<WidgetRepository, Widget, Str
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate mongoTemplate,
WidgetRepository widgetRepository) {
super(scheduler, validator, mongoConverter, mongoTemplate, widgetRepository);
WidgetRepository widgetRepository,
Analytics analytics,
SessionUserService sessionUserService) {
super(scheduler, validator, mongoConverter, mongoTemplate, widgetRepository, analytics, sessionUserService);
this.widgetRepository = widgetRepository;
}

View File

@ -4,12 +4,14 @@ spring.datasource.username=postgres
spring.datasource.password=root
spring.datasource.hikari.pool-name=Hikari-Eval-Pool
spring.datasource.hikari.maximum-pool-size=16
# JPA Properties
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
spring.jpa.show-sql=true
# Jackson Properties
spring.jackson.default-property-inclusion=non_null
# Segment Properties
segment.writeKey=FIRLqUgMYuTlyS6yqOE2hBGZs5umkWhr
com.rollbar.access-token=b91c4d5b9cac444088f4db9216ed6f42
com.rollbar.environment=development

View File

@ -0,0 +1,4 @@
dsn=https://5c388a93407a45a5886a941842e28d29@sentry.io/1758051
release=0.0.1
environment=local
stacktrace.hidecommon=false

View File

@ -9,6 +9,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
@ -37,6 +38,7 @@ public class OrganizationServiceTest {
/* Tests for the Create Organization Flow */
@Test
@WithMockUser(username = "api_user")
public void nullCreateOrganization() {
Mono<Organization> organizationResponse = organizationService.create(null);
StepVerifier.create(organizationResponse)
@ -46,6 +48,7 @@ public class OrganizationServiceTest {
}
@Test
@WithMockUser(username = "api_user")
public void nullName() {
organization.setName(null);
Mono<Organization> organizationResponse = organizationService.create(organization);
@ -56,8 +59,10 @@ public class OrganizationServiceTest {
}
@Test
@WithMockUser(username = "api_user")
public void validCreateOrganizationTest() {
Mono<Organization> organizationResponse = organizationService.create(organization);
Mono<Organization> organizationResponse = organizationService.create(organization)
.switchIfEmpty(Mono.error(new Exception("create is returning empty!!")));
StepVerifier.create(organizationResponse)
.assertNext(organization1 -> {
assertThat(organization1.getName()).isEqualTo("Test Name");
@ -68,6 +73,7 @@ public class OrganizationServiceTest {
/* Tests for Get Organization Flow */
@Test
@WithMockUser(username = "api_user")
public void getOrganizationInvalidId() {
Mono<Organization> organizationMono = organizationService.getById("random-id");
StepVerifier.create(organizationMono)
@ -77,6 +83,7 @@ public class OrganizationServiceTest {
}
@Test
@WithMockUser(username = "api_user")
public void getOrganizationNullId() {
Mono<Organization> organizationMono = organizationService.getById(null);
StepVerifier.create(organizationMono)
@ -86,6 +93,7 @@ public class OrganizationServiceTest {
}
@Test
@WithMockUser(username = "api_user")
public void validGetOrganizationByName() {
Mono<Organization> createOrganization = organizationService.create(organization);
Mono<Organization> getOrganization = createOrganization.flatMap(t -> organizationService.getById(t.getId()));
@ -100,6 +108,7 @@ public class OrganizationServiceTest {
/* Tests for Update Organization Flow */
@Test
@WithMockUser(username = "api_user")
public void validUpdateOrganization() {
Organization organization = new Organization();
organization.setName("Test Name");