Migrating from Tenant nomenclature to Organization.

This makes it consistent with the terminology that we want in the product. The hierarchy will be organization -> team -> user.
This commit is contained in:
Arpit Mohan 2019-09-04 15:08:40 +05:30
parent b7c480801f
commit f71bd9a4e0
31 changed files with 397 additions and 397 deletions

View File

@ -3,7 +3,7 @@ package com.appsmith.server.configurations;
import com.appsmith.server.domains.LoginSource;
import com.appsmith.server.domains.User;
import com.appsmith.server.domains.UserState;
import com.appsmith.server.services.TenantService;
import com.appsmith.server.services.OrganizationService;
import com.appsmith.server.services.UserService;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
@ -46,10 +46,10 @@ public class ClientUserRepository implements ServerOAuth2AuthorizedClientReposit
WebSessionServerOAuth2AuthorizedClientRepository.class.getName() + ".AUTHORIZED_CLIENTS";
private final String sessionAttributeName = DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME;
UserService userService;
TenantService tenantService;
public ClientUserRepository(UserService userService, TenantService tenantService) {
OrganizationService organizationService;
public ClientUserRepository(UserService userService, OrganizationService organizationService) {
this.userService = userService;
this.tenantService = tenantService;
this.organizationService = organizationService;
}
@Override
@ -93,15 +93,15 @@ public class ClientUserRepository implements ServerOAuth2AuthorizedClientReposit
newUser.setIsEnabled(true);
/** TODO
* Tenant here is being hardcoded. This is a stop gap measure
* A flow needs to be added to connect a User to a Tenant. Once
* that is done, during the login, the tenant should be picked up
* Organization here is being hardcoded. This is a stop gap measure
* A flow needs to be added to connect a User to a Organization. Once
* that is done, during the login, the organization should be picked up
* and a user should be hence created.
*/
return tenantService.findById("5d3e90a2dfec7c00047a81ea")
.map(tenant -> {
newUser.setTenant(tenant);
return organizationService.findById("5d3e90a2dfec7c00047a81ea")
.map(organization -> {
newUser.setOrganization(organization);
return newUser;
})
.then(userService.findByEmail(user.getEmail()))

View File

@ -2,7 +2,7 @@ package com.appsmith.server.configurations;
import com.appsmith.server.constants.Security;
import com.appsmith.server.services.TenantService;
import com.appsmith.server.services.OrganizationService;
import com.appsmith.server.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@ -27,7 +27,7 @@ public class SecurityConfig {
private UserService userService;
@Autowired
private TenantService tenantService;
private OrganizationService organizationService;
/**
* This configuration enables CORS requests for the most common HTTP Methods
@ -72,7 +72,7 @@ public class SecurityConfig {
.authenticated()
.and().httpBasic()
.and().oauth2Login()
.authorizedClientRepository(new ClientUserRepository(userService, tenantService))
.authorizedClientRepository(new ClientUserRepository(userService, organizationService))
.and().formLogin()
.and().build();
}

View File

@ -1,7 +1,7 @@
package com.appsmith.server.constants;
public class FieldName {
public static String TENANT = "tenant";
public static String ORGANIZATION = "organization";
public static String ID = "id";
public static String NAME = "name";
}

View File

@ -4,7 +4,7 @@ public interface Url {
String BASE_URL = "/api";
String VERSION = "/v1";
String WIDGET_URL = BASE_URL + VERSION + "/widgets";
String TENANT_URL = BASE_URL + VERSION + "/tenants";
String ORGANIZATION_URL = BASE_URL + VERSION + "/organizations";
String LAYOUT_URL = BASE_URL + VERSION + "/layouts";
String PLUGIN_URL = BASE_URL + VERSION + "/plugins";
String QUERY_URL = BASE_URL + VERSION + "/queries";

View File

@ -0,0 +1,20 @@
package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.services.OrganizationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(Url.ORGANIZATION_URL)
public class OrganizationController extends BaseController<OrganizationService, Organization, String> {
@Autowired
public OrganizationController(OrganizationService organizationService) {
super(organizationService);
}
}

View File

@ -1,9 +1,9 @@
package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.Tenant;
import com.appsmith.server.dtos.PluginTenantDTO;
import com.appsmith.server.dtos.PluginOrgDTO;
import com.appsmith.server.dtos.ResponseDto;
import com.appsmith.server.services.PluginService;
import org.springframework.beans.factory.annotation.Autowired;
@ -29,16 +29,16 @@ public class PluginController extends BaseController<PluginService, Plugin, Stri
@PostMapping("/install")
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDto<Tenant>> install(@Valid @RequestBody PluginTenantDTO plugin) {
public Mono<ResponseDto<Organization>> install(@Valid @RequestBody PluginOrgDTO plugin) {
return service.installPlugin(plugin)
.map(tenant -> new ResponseDto<>(HttpStatus.CREATED.value(), tenant, null));
.map(organization -> new ResponseDto<>(HttpStatus.CREATED.value(), organization, null));
}
@PostMapping("/uninstall")
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDto<Tenant>> uninstall(@Valid @RequestBody PluginTenantDTO plugin) {
public Mono<ResponseDto<Organization>> uninstall(@Valid @RequestBody PluginOrgDTO plugin) {
return service.uninstallPlugin(plugin)
.map(tenant -> new ResponseDto<>(HttpStatus.CREATED.value(), tenant, null));
.map(organization -> new ResponseDto<>(HttpStatus.CREATED.value(), organization, null));
}
}

View File

@ -1,20 +0,0 @@
package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Tenant;
import com.appsmith.server.services.TenantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(Url.TENANT_URL)
public class TenantController extends BaseController<TenantService, Tenant, String> {
@Autowired
public TenantController(TenantService tenantService) {
super(tenantService);
}
}

View File

@ -15,7 +15,7 @@ import java.util.List;
@ToString
@NoArgsConstructor
@Document
public class Tenant extends BaseDomain {
public class Organization extends BaseDomain {
private String domain;
@ -24,8 +24,8 @@ public class Tenant extends BaseDomain {
private String website;
private List<TenantSetting> tenantSettings;
private List<OrganizationSetting> organizationSettings;
private List<TenantPlugin> plugins;
private List<OrganizationPlugin> plugins;
}

View File

@ -1,6 +1,6 @@
package com.appsmith.server.domains;
import com.appsmith.server.dtos.TenantPluginStatus;
import com.appsmith.server.dtos.OrganizationPluginStatus;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@ -10,10 +10,10 @@ import lombok.ToString;
@Setter
@ToString
@NoArgsConstructor
public class TenantPlugin extends BaseDomain {
public class OrganizationPlugin extends BaseDomain {
private String pluginId;
TenantPluginStatus status;
OrganizationPluginStatus status;
}

View File

@ -15,7 +15,7 @@ import org.springframework.data.mongodb.core.mapping.DBRef;
@Setter
@ToString
@NoArgsConstructor
public class TenantSetting extends BaseDomain {
public class OrganizationSetting extends BaseDomain {
@DBRef
private Setting setting;

View File

@ -18,7 +18,7 @@ public class Resource extends BaseDomain {
String pluginId;
String tenantId;
String organizationId;
ResourceConfiguration resourceConfiguration;

View File

@ -19,6 +19,6 @@ public class Setting extends BaseDomain {
private String defaultValue;
private Boolean isTenantSetting;
private Boolean isOrganizationSetting;
}

View File

@ -35,7 +35,7 @@ public class User extends BaseDomain implements UserDetails {
private Boolean isEnabled;
private Tenant tenant;
private Organization organization;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {

View File

@ -1,5 +1,5 @@
package com.appsmith.server.dtos;
public enum TenantPluginStatus {
public enum OrganizationPluginStatus {
FREE, TRIAL, ACTIVATED
}

View File

@ -5,7 +5,9 @@ import lombok.Setter;
@Getter
@Setter
public class PluginTenantDTO {
public class PluginOrgDTO {
String pluginId;
TenantPluginStatus status;
OrganizationPluginStatus status;
}

View File

@ -9,7 +9,7 @@ public enum AppsmithError {
NO_RESOURCE_FOUND(404, 1000, "Unable to find {0} with id {1}"),
INVALID_PARAMETER(400, 4000, "Invalid parameter {0} provided in the input"),
PLUGIN_NOT_INSTALLED(400, 4001, "Plugin not installed for tenant {0}"),
PLUGIN_NOT_INSTALLED(400, 4001, "Plugin not installed for organization {0}"),
PLUGIN_ID_NOT_GIVEN(400, 4002, "Missing plugin id. Please input correct plugin id"),
RESOURCE_ID_NOT_GIVEN(400, 4003, "Missing resource id. Please input correct resource id"),
INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request");

View File

@ -0,0 +1,12 @@
package com.appsmith.server.repositories;
import com.appsmith.server.domains.Organization;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
@Repository
public interface OrganizationRepository extends BaseRepository<Organization, String> {
Mono<Organization> findByName(String name);
Mono<Organization> findByIdAndPluginsPluginId(String organizationId, String pluginId);
}

View File

@ -1,12 +0,0 @@
package com.appsmith.server.repositories;
import com.appsmith.server.domains.Tenant;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
@Repository
public interface TenantRepository extends BaseRepository<Tenant, String> {
Mono<Tenant> findByName(String name);
Mono<Tenant> findByIdAndPluginsPluginId(String tenantId, String pluginId);
}

View File

@ -16,10 +16,8 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.Map;
import java.util.Set;
public abstract class BaseService<R extends BaseRepository, T extends BaseDomain, ID> implements CrudService<T, ID> {

View File

@ -0,0 +1,17 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Organization;
import reactor.core.publisher.Mono;
public interface OrganizationService extends CrudService<Organization, String> {
Mono<Organization> getByName(String name);
Mono<Organization> create(Organization object);
Mono<Organization> findById(String id);
Mono<Organization> save(Organization organization);
Mono<Organization> findByIdAndPluginsPluginId(String organizationId, String pluginId);
}

View File

@ -0,0 +1,110 @@
package com.appsmith.server.services;
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.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.OrganizationRepository;
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.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class OrganizationServiceImpl extends BaseService<OrganizationRepository, Organization, String> implements OrganizationService {
private final OrganizationRepository repository;
private final SettingService settingService;
@Autowired
public OrganizationServiceImpl(Scheduler scheduler,
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
OrganizationRepository repository,
SettingService settingService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.repository = repository;
this.settingService = settingService;
}
@Override
public Mono<Organization> getByName(String name) {
return repository.findByName(name);
}
/*
* Create needs to first fetch and embed Setting in OrganizationSetting
* for the settings that have diverged from the default. Once the
* settings has been embedded in all the organization settings, the library
* function is called to store the enhanced organization object.
*/
@Override
public Mono<Organization> create(Organization organization) {
if (organization == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORGANIZATION));
}
log.debug("Going to create the organization {}", organization.getName());
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);
}
private Mono<Organization> enhanceOrganizationSettingList(Organization organization) {
if (organization.getOrganizationSettings() == null) {
organization.setOrganizationSettings(new ArrayList<>());
}
Flux<OrganizationSetting> organizationSettingFlux = Flux.fromIterable(organization.getOrganizationSettings());
// For each organization setting, fetch and embed the setting, and once all the organization setting are done, collect it
// back into a single list of organization settings.
Mono<List<OrganizationSetting>> listMono = organizationSettingFlux.flatMap(this::fetchAndEmbedSetting).collectList();
return listMono.map(list -> {
organization.setOrganizationSettings(list);
return list;
}).thenReturn(organization);
}
private Mono<OrganizationSetting> fetchAndEmbedSetting(OrganizationSetting organizationSetting) {
String key = organizationSetting.getSetting().getKey();
Mono<Setting> setting = settingService.getByKey(key);
return setting.map(setting1 -> {
organizationSetting.setSetting(setting1);
return organizationSetting;
});
}
@Override
public Mono<Organization> findById(String id) {
return repository.findById(id);
}
@Override
public Mono<Organization> save(Organization organization) {
return repository.save(organization);
}
@Override
public Mono<Organization> findByIdAndPluginsPluginId(String organizationId, String pluginId) {
return repository.findByIdAndPluginsPluginId(organizationId, pluginId);
}
}

View File

@ -1,9 +1,9 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.PluginType;
import com.appsmith.server.domains.Tenant;
import com.appsmith.server.dtos.PluginTenantDTO;
import com.appsmith.server.dtos.PluginOrgDTO;
import reactor.core.publisher.Mono;
public interface PluginService extends CrudService<Plugin, String> {
@ -18,9 +18,9 @@ public interface PluginService extends CrudService<Plugin, String> {
*/
PluginExecutor getPluginExecutor(PluginType pluginType, String className);
public Mono<Tenant> installPlugin(PluginTenantDTO plugin);
public Mono<Organization> installPlugin(PluginOrgDTO plugin);
public Mono<Tenant> uninstallPlugin(PluginTenantDTO plugin);
public Mono<Organization> uninstallPlugin(PluginOrgDTO plugin);
public Mono<Plugin> findByName(String name);

View File

@ -1,12 +1,12 @@
package com.appsmith.server.services;
import com.appsmith.server.configurations.ClientUserRepository;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.OrganizationPlugin;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.PluginType;
import com.appsmith.server.domains.Tenant;
import com.appsmith.server.domains.TenantPlugin;
import com.appsmith.server.dtos.PluginTenantDTO;
import com.appsmith.server.dtos.TenantPluginStatus;
import com.appsmith.server.dtos.OrganizationPluginStatus;
import com.appsmith.server.dtos.PluginOrgDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.PluginRepository;
@ -33,10 +33,10 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
private final UserRepository userRepository;
private final ApplicationContext applicationContext;
private final ClientUserRepository clientUserRepository;
private final TenantService tenantService;
private final OrganizationService organizationService;
@Value("${tenant.id}")
private String tenantId;
@Value("${organization.id}")
private String organizationId;
@Autowired
public PluginServiceImpl(Scheduler scheduler,
@ -47,13 +47,13 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
UserRepository userRepository,
ApplicationContext applicationContext,
ClientUserRepository clientUserRepository,
TenantService tenantService) {
OrganizationService organizationService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.userRepository = userRepository;
this.applicationContext = applicationContext;
pluginRepository = repository;
this.clientUserRepository = clientUserRepository;
this.tenantService = tenantService;
this.organizationService = organizationService;
}
public PluginExecutor getPluginExecutor(PluginType pluginType, String className) {
@ -78,88 +78,88 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
}
@Override
public Mono<Tenant> installPlugin(PluginTenantDTO pluginTenantDTO) {
if (pluginTenantDTO.getPluginId() == null) {
public Mono<Organization> installPlugin(PluginOrgDTO pluginOrgDTO) {
if (pluginOrgDTO.getPluginId() == null) {
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
}
return Mono.just(pluginTenantDTO)
.flatMap(plugin -> storeTenantPlugin(plugin, pluginTenantDTO.getStatus()))
return Mono.just(pluginOrgDTO)
.flatMap(plugin -> storeOrganizationPlugin(plugin, pluginOrgDTO.getStatus()))
.switchIfEmpty(Mono.empty());
}
@Override
public Mono<Tenant> uninstallPlugin(PluginTenantDTO pluginDTO) {
public Mono<Organization> uninstallPlugin(PluginOrgDTO pluginDTO) {
/*TODO
* Tenant & user association is being mocked here by forcefully
* only using a hardcoded tenant. This needs to be replaced by
* a user-tenant association flow. The Tenant needs to be picked
* Organization & user association is being mocked here by forcefully
* only using a hardcoded organization. This needs to be replaced by
* a user-organization association flow. The Organization needs to be picked
* up from a user object. This is being used in install/uninstall
* plugin from a tenant flow. Instead, the current user should be read
* plugin from a organization flow. Instead, the current user should be read
* using the following :
* ReactiveSecurityContextHolder.getContext()
* .map(SecurityContext::getAuthentication)
* .map(Authentication::getPrincipal);
* Once the user has been pulled using this, tenant should already
* be stored as part of user and this tenant should be used to store
* Once the user has been pulled using this, organization should already
* be stored as part of user and this organization should be used to store
* the installed plugin or to delete plugin during uninstallation.
*/
if (pluginDTO.getPluginId() == null) {
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
}
//Find the tenant using id and plugin id -> This is to find if the tenant has the plugin installed
Mono<Tenant> tenantMono = tenantService.findByIdAndPluginsPluginId(tenantId, pluginDTO.getPluginId());
//Find the organization using id and plugin id -> This is to find if the organization has the plugin installed
Mono<Organization> organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, pluginDTO.getPluginId());
return tenantMono
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, tenantId)))
//In case the plugin is not found for the tenant, the tenantMono would not emit and the rest of the flow would stop
//i.e. the rest of the code flow would only happen when there is a plugin found for the tenant that can
return organizationMono
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, organizationId)))
//In case the plugin is not found for the organization, the organizationMono would not emit and the rest of the flow would stop
//i.e. the rest of the code flow would only happen when there is a plugin found for the organization that can
//be uninstalled.
.map(tenant -> {
List<TenantPlugin> tenantPluginList = tenant.getPlugins();
tenantPluginList.removeIf(listPlugin -> listPlugin.getPluginId().equals(pluginDTO.getPluginId()));
tenant.setPlugins(tenantPluginList);
return tenant;
.map(organization -> {
List<OrganizationPlugin> organizationPluginList = organization.getPlugins();
organizationPluginList.removeIf(listPlugin -> listPlugin.getPluginId().equals(pluginDTO.getPluginId()));
organization.setPlugins(organizationPluginList);
return organization;
})
.flatMap(tenantService::save);
.flatMap(organizationService::save);
}
private Mono<Tenant> storeTenantPlugin(PluginTenantDTO pluginDTO, TenantPluginStatus status) {
private Mono<Organization> storeOrganizationPlugin(PluginOrgDTO pluginDTO, OrganizationPluginStatus status) {
/*TODO
* Tenant & user association is being mocked here by forcefully
* only using a hardcoded tenant. This needs to be replaced by
* a user-tenant association flow. The Tenant needs to be picked
* Organization & user association is being mocked here by forcefully
* only using a hardcoded organization. This needs to be replaced by
* a user-organization association flow. The Organization needs to be picked
* up from a user object. This is being used in install/uninstall
* plugin from a tenant flow. Instead, the current user should be read
* plugin from a organization flow. Instead, the current user should be read
* using the following :
* ReactiveSecurityContextHolder.getContext()
* .map(SecurityContext::getAuthentication)
* .map(Authentication::getPrincipal);
* Once the user has been pulled using this, tenant should already
* be stored as part of user and this tenant should be used to store
* Once the user has been pulled using this, organization should already
* be stored as part of user and this organization should be used to store
* the installed plugin or to delete plugin during uninstalling.
*/
//Find the tenant using id and plugin id -> This is to find if the tenant already has the plugin installed
Mono<Tenant> tenantMono = tenantService.findByIdAndPluginsPluginId(tenantId, pluginDTO.getPluginId());
//Find the organization using id and plugin id -> This is to find if the organization already has the plugin installed
Mono<Organization> organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, pluginDTO.getPluginId());
return tenantMono
return organizationMono
.switchIfEmpty(Mono.defer(() -> {
//If the plugin is not found in the tenant, its not already installed. Install now.
return tenantService.findById(tenantId).map(tenant -> {
List<TenantPlugin> tenantPluginList = tenant.getPlugins();
if (tenantPluginList == null) {
tenantPluginList = new ArrayList<TenantPlugin>();
//If the plugin is not found in the organization, its not already installed. Install now.
return organizationService.findById(organizationId).map(organization -> {
List<OrganizationPlugin> organizationPluginList = organization.getPlugins();
if (organizationPluginList == null) {
organizationPluginList = new ArrayList<OrganizationPlugin>();
}
log.debug("Installing plugin {} for tenant {}", pluginDTO.getPluginId(), tenant.getName());
TenantPlugin tenantPlugin = new TenantPlugin();
tenantPlugin.setPluginId(pluginDTO.getPluginId());
tenantPlugin.setStatus(status);
tenantPluginList.add(tenantPlugin);
tenant.setPlugins(tenantPluginList);
return tenant;
}).flatMap(tenantService::save);
log.debug("Installing plugin {} for organization {}", pluginDTO.getPluginId(), organization.getName());
OrganizationPlugin organizationPlugin = new OrganizationPlugin();
organizationPlugin.setPluginId(pluginDTO.getPluginId());
organizationPlugin.setStatus(status);
organizationPluginList.add(organizationPlugin);
organization.setPlugins(organizationPluginList);
return organization;
}).flatMap(organizationService::save);
}));
}

View File

@ -1,7 +1,7 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.domains.Resource;
import com.appsmith.server.domains.Tenant;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.ResourceRepository;
@ -21,18 +21,18 @@ import javax.validation.constraints.NotNull;
@Service
public class ResourceServiceImpl extends BaseService<ResourceRepository, Resource, String> implements ResourceService {
@Value("${tenant.id}")
private String tenantId;
@Value("${organization.id}")
private String organizationId;
private final ResourceRepository repository;
private final TenantService tenantService;
private final OrganizationService organizationService;
private final PluginService pluginService;
@Autowired
public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, TenantService tenantService, PluginService pluginService) {
public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, OrganizationService organizationService, PluginService pluginService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.repository = repository;
this.tenantService = tenantService;
this.organizationService = organizationService;
this.pluginService = pluginService;
}
@ -44,17 +44,17 @@ public class ResourceServiceImpl extends BaseService<ResourceRepository, Resourc
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
}
Mono<Tenant> tenantMono = tenantService.findByIdAndPluginsPluginId(tenantId, resource.getPluginId());
Mono<Organization> organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, resource.getPluginId());
//Add tenant id to the resource.
//Add organization id to the resource.
Mono<Resource> updatedResourceMono = Mono.just(resource)
.map(updatedResource -> {
updatedResource.setTenantId(tenantId);
updatedResource.setOrganizationId(organizationId);
return updatedResource;
});
return tenantMono
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, tenantId)))
return organizationMono
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, organizationId)))
.then(updatedResourceMono)
.flatMap(repository::save);
}

View File

@ -1,17 +0,0 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Tenant;
import reactor.core.publisher.Mono;
public interface TenantService extends CrudService<Tenant, String> {
Mono<Tenant> getByName(String name);
Mono<Tenant> create(Tenant object);
Mono<Tenant> findById(String id);
Mono<Tenant> save(Tenant tenant);
Mono<Tenant> findByIdAndPluginsPluginId(String tenantId, String pluginId);
}

View File

@ -1,110 +0,0 @@
package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Setting;
import com.appsmith.server.domains.Tenant;
import com.appsmith.server.domains.TenantSetting;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.TenantRepository;
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.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class TenantServiceImpl extends BaseService<TenantRepository, Tenant, String> implements TenantService {
private final TenantRepository repository;
private final SettingService settingService;
@Autowired
public TenantServiceImpl(Scheduler scheduler,
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
TenantRepository repository,
SettingService settingService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.repository = repository;
this.settingService = settingService;
}
@Override
public Mono<Tenant> getByName(String name) {
return repository.findByName(name);
}
/*
* Create needs to first fetch and embed Setting in TenantSetting
* for the settings that have diverged from the default. Once the
* settings has been embedded in all the tenant settings, the library
* function is called to store the enhanced tenant object.
*/
@Override
public Mono<Tenant> create(Tenant tenant) {
if (tenant == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.TENANT));
}
log.debug("Going to create the tenant {}", tenant.getName());
return Mono.just(tenant)
.flatMap(this::validateObject)
//transform the tenant data to embed setting object in each object in tenantSetting list.
.flatMap(this::enhanceTenantSettingList)
//Call the library function to save the updated tenant
.flatMap(repository::save);
}
private Mono<Tenant> enhanceTenantSettingList(Tenant tenant) {
if (tenant.getTenantSettings() == null) {
tenant.setTenantSettings(new ArrayList<>());
}
Flux<TenantSetting> tenantSettingFlux = Flux.fromIterable(tenant.getTenantSettings());
// For each tenant setting, fetch and embed the setting, and once all the tenant setting are done, collect it
// back into a single list of tenant settings.
Mono<List<TenantSetting>> listMono = tenantSettingFlux.flatMap(this::fetchAndEmbedSetting).collectList();
return listMono.map(list -> {
tenant.setTenantSettings(list);
return list;
}).thenReturn(tenant);
}
private Mono<TenantSetting> fetchAndEmbedSetting(TenantSetting tenantSetting) {
String key = tenantSetting.getSetting().getKey();
Mono<Setting> setting = settingService.getByKey(key);
return setting.map(setting1 -> {
tenantSetting.setSetting(setting1);
return tenantSetting;
});
}
@Override
public Mono<Tenant> findById(String id) {
return repository.findById(id);
}
@Override
public Mono<Tenant> save(Tenant tenant) {
return repository.save(tenant);
}
@Override
public Mono<Tenant> findByIdAndPluginsPluginId(String tenantId, String pluginId) {
return repository.findByIdAndPluginsPluginId(tenantId, pluginId);
}
}

View File

@ -19,5 +19,5 @@ jdbc.postgres.password=root
spring.security.oauth2.client.registration.google.client-id=869021686091-9b84bbf7ea683t1aaefqnmefcnmk6fq6.apps.googleusercontent.com
spring.security.oauth2.client.registration.google.client-secret=9dvITt4OayEY1HfeY8bHX74p
#Hard coded tenant properties
tenant.id=5d64f1f594a5d31b3ee9ca16
#Hard coded organization properties
organization.id=5d64f1f594a5d31b3ee9ca16

View File

@ -17,5 +17,5 @@ jdbc.postgres.password=09275163cd7e737baf4c210b5e8db8ed88ddb7a0ee9acc82416fd7534
spring.security.oauth2.client.registration.google.client-id=869021686091-9b84bbf7ea683t1aaefqnmefcnmk6fq6.apps.googleusercontent.com
spring.security.oauth2.client.registration.google.client-secret=9dvITt4OayEY1HfeY8bHX74p
#Hard coded tenant properties
tenant.id=5d3e90a2dfec7c00047a81ea
#Hard coded organization properties
organization.id=5d3e90a2dfec7c00047a81ea

View File

@ -14,5 +14,5 @@ spring.jpa.show-sql=true
# Jackson Properties
spring.jackson.default-property-inclusion=non_null
#Hard coded tenant properties
tenant.id=5d5e7c29006cb4e82ea75075
#Hard coded organization properties
organization.id=5d5e7c29006cb4e82ea75075

View File

@ -0,0 +1,125 @@
package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Organization;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import org.junit.Before;
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.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrganizationServiceTest {
@Autowired
OrganizationService organizationService;
Organization organization;
@Before
public void setup() {
organization = new Organization();
organization.setName("Test Name");
organization.setDomain("example.com");
organization.setWebsite("https://example.com");
}
/* Tests for the Create Organization Flow */
@Test
public void nullCreateOrganization() {
Mono<Organization> organizationResponse = organizationService.create(null);
StepVerifier.create(organizationResponse)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.ORGANIZATION)))
.verify();
}
@Test
public void nullName() {
organization.setName(null);
Mono<Organization> organizationResponse = organizationService.create(organization);
StepVerifier.create(organizationResponse)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.NAME)))
.verify();
}
@Test
public void validCreateOrganizationTest() {
Mono<Organization> organizationResponse = organizationService.create(organization);
StepVerifier.create(organizationResponse)
.assertNext(organization1 -> {
assertThat(organization1.getName()).isEqualTo("Test Name");
})
.verifyComplete();
}
/* Tests for Get Organization Flow */
@Test
public void getOrganizationInvalidId() {
Mono<Organization> organizationMono = organizationService.getById("random-id");
StepVerifier.create(organizationMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage("resource", "random-id")))
.verify();
}
@Test
public void getOrganizationNullId() {
Mono<Organization> organizationMono = organizationService.getById(null);
StepVerifier.create(organizationMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.ID)))
.verify();
}
@Test
public void validGetOrganizationByName() {
Mono<Organization> createOrganization = organizationService.create(organization);
Mono<Organization> getOrganization = createOrganization.flatMap(t -> organizationService.getById(t.getId()));
StepVerifier.create(getOrganization)
.assertNext(t -> {
assertThat(t).isNotNull();
assertThat(t.getName()).isEqualTo(organization.getName());
assertThat(t.getId()).isEqualTo(organization.getId());
})
.verifyComplete();
}
/* Tests for Update Organization Flow */
@Test
public void validUpdateOrganization() {
Organization organization = new Organization();
organization.setName("Test Name");
organization.setDomain("example.com");
organization.setWebsite("https://example.com");
Mono<Organization> createOrganization = organizationService.create(organization);
Mono<Organization> updateOrganization = createOrganization
.map(t -> {
t.setDomain("abc.com");
return t;
})
.flatMap(t -> organizationService.update(t.getId(), t))
.flatMap(t -> organizationService.getById(t.getId()));
StepVerifier.create(updateOrganization)
.assertNext(t -> {
assertThat(t).isNotNull();
assertThat(t.getName()).isEqualTo(organization.getName());
assertThat(t.getId()).isEqualTo(organization.getId());
assertThat(t.getDomain()).isEqualTo("abc.com");
})
.verifyComplete();
}
}

View File

@ -1,125 +0,0 @@
package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Tenant;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import org.junit.Before;
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.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TenantServiceTest {
@Autowired
TenantService tenantService;
Tenant tenant;
@Before
public void setup() {
tenant = new Tenant();
tenant.setName("Test Name");
tenant.setDomain("example.com");
tenant.setWebsite("https://example.com");
}
/* Tests for the Create Tenant Flow */
@Test
public void nullCreateTenant() {
Mono<Tenant> tenantResponse = tenantService.create(null);
StepVerifier.create(tenantResponse)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.TENANT)))
.verify();
}
@Test
public void nullName() {
tenant.setName(null);
Mono<Tenant> tenantResponse = tenantService.create(tenant);
StepVerifier.create(tenantResponse)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.NAME)))
.verify();
}
@Test
public void validCreateTenantTest() {
Mono<Tenant> tenantResponse = tenantService.create(tenant);
StepVerifier.create(tenantResponse)
.assertNext(tenant1 -> {
assertThat(tenant1.getName()).isEqualTo("Test Name");
})
.verifyComplete();
}
/* Tests for Get Tenant Flow */
@Test
public void getTenantInvalidId() {
Mono<Tenant> tenantMono = tenantService.getById("random-id");
StepVerifier.create(tenantMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage("resource", "random-id")))
.verify();
}
@Test
public void getTenantNullId() {
Mono<Tenant> tenantMono = tenantService.getById(null);
StepVerifier.create(tenantMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.ID)))
.verify();
}
@Test
public void validGetTenantByName() {
Mono<Tenant> createTenant = tenantService.create(tenant);
Mono<Tenant> getTenant = createTenant.flatMap(t -> tenantService.getById(t.getId()));
StepVerifier.create(getTenant)
.assertNext(t -> {
assertThat(t).isNotNull();
assertThat(t.getName()).isEqualTo(tenant.getName());
assertThat(t.getId()).isEqualTo(tenant.getId());
})
.verifyComplete();
}
/* Tests for Update Tenant Flow */
@Test
public void validUpdateTenant() {
Tenant tenant = new Tenant();
tenant.setName("Test Name");
tenant.setDomain("example.com");
tenant.setWebsite("https://example.com");
Mono<Tenant> createTenant = tenantService.create(tenant);
Mono<Tenant> updateTenant = createTenant
.map(t -> {
t.setDomain("abc.com");
return t;
})
.flatMap(t -> tenantService.update(t.getId(), t))
.flatMap(t -> tenantService.getById(t.getId()));
StepVerifier.create(updateTenant)
.assertNext(t -> {
assertThat(t).isNotNull();
assertThat(t.getName()).isEqualTo(tenant.getName());
assertThat(t.getId()).isEqualTo(tenant.getId());
assertThat(t.getDomain()).isEqualTo("abc.com");
})
.verifyComplete();
}
}