Use sequences for numbering new datasources.

This commit is contained in:
Shrikant Kandula 2020-05-20 11:21:32 +00:00 committed by Arpit Mohan
parent b13a7a5df3
commit 7037d99cfa
9 changed files with 125 additions and 25 deletions

View File

@ -20,6 +20,10 @@ import java.util.Set;
@NoArgsConstructor
@Document
public class Datasource extends BaseDomain {
@Transient
public static final String DEFAULT_NAME_PREFIX = "Untitled Datasource";
String name;
String pluginId;

View File

@ -0,0 +1,12 @@
package com.appsmith.server.domains;
import lombok.Getter;
public class Sequence {
private String name;
@Getter
private Long nextNumber;
}

View File

@ -17,6 +17,7 @@ import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.PluginType;
import com.appsmith.server.domains.Query;
import com.appsmith.server.domains.Role;
import com.appsmith.server.domains.Sequence;
import com.appsmith.server.domains.Setting;
import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.OrganizationPluginStatus;
@ -31,7 +32,6 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.index.CompoundIndexDefinition;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.util.CollectionUtils;
import java.time.Instant;
@ -42,6 +42,10 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
import static org.springframework.data.mongodb.core.query.Update.update;
@Slf4j
@ChangeLog(order = "001")
public class DatabaseChangelog {
@ -263,7 +267,7 @@ public class DatabaseChangelog {
@ChangeSet(order = "006", id = "hide-rapidapi-plugin", author = "")
public void hideRapidApiPluginFromCreateDatasource(MongoTemplate mongoTemplate) {
final Plugin rapidApiPlugin = mongoTemplate.findOne(
org.springframework.data.mongodb.core.query.Query.query(Criteria.where("packageName").is("rapidapi-plugin")),
query(where("packageName").is("rapidapi-plugin")),
Plugin.class
);
@ -341,7 +345,7 @@ public class DatabaseChangelog {
@ChangeSet(order = "011", id = "install-default-plugins-to-all-organizations", author = "")
public void installDefaultPluginsToAllOrganizations(MongoTemplate mongoTemplate) {
final List<Plugin> defaultPlugins = mongoTemplate.find(
org.springframework.data.mongodb.core.query.Query.query(Criteria.where("defaultInstall").is(true)),
query(where("defaultInstall").is(true)),
Plugin.class
);
@ -369,7 +373,7 @@ public class DatabaseChangelog {
@ChangeSet(order = "012", id = "ensure-datasource-created-and-updated-at-fields", author = "")
public void ensureDatasourceCreatedAndUpdatedAt(MongoTemplate mongoTemplate) {
final List<Datasource> missingCreatedAt = mongoTemplate.find(
org.springframework.data.mongodb.core.query.Query.query(Criteria.where("createdAt").exists(false)),
query(where("createdAt").exists(false)),
Datasource.class
);
@ -379,7 +383,7 @@ public class DatabaseChangelog {
}
final List<Datasource> missingUpdatedAt = mongoTemplate.find(
org.springframework.data.mongodb.core.query.Query.query(Criteria.where("updatedAt").exists(false)),
query(where("updatedAt").exists(false)),
Datasource.class
);
@ -389,4 +393,29 @@ public class DatabaseChangelog {
}
}
@ChangeSet(order = "013", id = "add-index-for-sequence-name", author = "")
public void addIndexForSequenceName(MongoTemplate mongoTemplate) {
ensureIndexes(mongoTemplate, Sequence.class,
makeIndex(FieldName.NAME).unique()
);
}
@ChangeSet(order = "014", id = "set-initial-sequence-for-datasource", author = "")
public void setInitialSequenceForDatasource(MongoTemplate mongoTemplate) {
final Long maxUntitledDatasourceNumber = mongoTemplate.find(
query(where(FieldName.NAME).regex("^" + Datasource.DEFAULT_NAME_PREFIX + " \\d+$")),
Datasource.class
)
.stream()
.map(datasource -> Long.parseLong(datasource.getName().split(" ")[2]))
.max(Long::compareTo)
.orElse(0L);
mongoTemplate.upsert(
query(where(FieldName.NAME).is(mongoTemplate.getCollectionName(Datasource.class))),
update("nextNumber", maxUntitledDatasourceNumber + 1),
Sequence.class
);
}
}

View File

@ -1,8 +1,6 @@
package com.appsmith.server.repositories;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Datasource;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -15,6 +13,4 @@ public interface DatasourceRepository extends BaseRepository<Datasource, String>
Flux<Datasource> findAllByOrganizationId(String organizationId);
@Query(value = "{" + FieldName.NAME + ": {$regex: ?0}}", count = true)
Mono<Long> countNamesByPrefix(String namePrefix);
}

View File

@ -16,7 +16,6 @@ public interface DatasourceService extends CrudService<Datasource, String> {
Set<String> extractKeysFromDatasource(Datasource datasource);
Mono<String> getNextUniqueName(String initialSlug);
Mono<Datasource> validateDatasource(Datasource datasource);
}

View File

@ -42,6 +42,7 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
private final ObjectMapper objectMapper;
private final PluginService pluginService;
private final PluginExecutorHelper pluginExecutorHelper;
private final SequenceService sequenceService;
@Autowired
public DatasourceServiceImpl(Scheduler scheduler,
@ -54,7 +55,8 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
SessionUserService sessionUserService,
ObjectMapper objectMapper,
PluginService pluginService,
PluginExecutorHelper pluginExecutorHelper) {
PluginExecutorHelper pluginExecutorHelper,
SequenceService sequenceService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.repository = repository;
this.organizationService = organizationService;
@ -62,6 +64,7 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
this.objectMapper = objectMapper;
this.pluginService = pluginService;
this.pluginExecutorHelper = pluginExecutorHelper;
this.sequenceService = sequenceService;
}
@Override
@ -73,21 +76,16 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
Mono<Datasource> datasourceMono = Mono.just(datasource);
if (StringUtils.isEmpty(datasource.getName())) {
datasourceMono = datasourceMono.zipWith(
getNextUniqueName("Untitled datasource"),
(datasource1, name) -> {
datasource1.setName(name);
datasourceMono = sequenceService
.getNextAsSuffix(Datasource.class)
.zipWith(datasourceMono, (sequenceNumber, datasource1) -> {
datasource1.setName(Datasource.DEFAULT_NAME_PREFIX + sequenceNumber);
return datasource1;
});
}
return datasourceMono.flatMap(this::validateAndSaveDatasourceToRepository);
}
@Override
public Mono<String> getNextUniqueName(String namePrefix) {
return repository.countNamesByPrefix(namePrefix)
.map(max -> namePrefix + (max == 0 ? "" : " " + (max + 1)));
return datasourceMono
.flatMap(this::validateAndSaveDatasourceToRepository);
}
@Override

View File

@ -0,0 +1,14 @@
package com.appsmith.server.services;
import com.appsmith.external.models.BaseDomain;
import reactor.core.publisher.Mono;
public interface SequenceService {
Mono<Long> getNext(String name);
Mono<Long> getNext(Class<? extends BaseDomain> domainClass);
Mono<String> getNextAsSuffix(Class<? extends BaseDomain> domainClass);
}

View File

@ -0,0 +1,48 @@
package com.appsmith.server.services;
import com.appsmith.external.models.BaseDomain;
import com.appsmith.server.domains.Sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import static org.springframework.data.mongodb.core.FindAndModifyOptions.options;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
@Service
public class SequenceServiceImpl implements SequenceService {
private final ReactiveMongoTemplate mongoTemplate;
@Autowired
public SequenceServiceImpl(ReactiveMongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@Override
public Mono<Long> getNext(String name) {
return mongoTemplate
.findAndModify(
query(where("name").is(name)),
new Update().inc("nextNumber", 1),
options().upsert(true),
Sequence.class
)
.map(Sequence::getNextNumber);
}
@Override
public Mono<Long> getNext(Class<? extends BaseDomain> domainClass) {
return getNext(mongoTemplate.getCollectionName(domainClass));
}
@Override
public Mono<String> getNextAsSuffix(Class<? extends BaseDomain> domainClass) {
return getNext(mongoTemplate.getCollectionName(domainClass))
.map(number -> number > 1 ? " " + number : "");
}
}

View File

@ -253,12 +253,12 @@ public class DatasourceServiceTest {
final Datasource ds1 = tuple2.getT1();
assertThat(ds1.getId()).isNotEmpty();
assertThat(ds1.getPluginId()).isEqualTo(datasource1.getPluginId());
assertThat(ds1.getName()).isEqualTo("Untitled datasource");
assertThat(ds1.getName()).isEqualTo("Untitled Datasource");
final Datasource ds2 = tuple2.getT2();
assertThat(ds2.getId()).isNotEmpty();
assertThat(ds2.getPluginId()).isEqualTo(datasource1.getPluginId());
assertThat(ds2.getName()).isEqualTo("Untitled datasource 2");
assertThat(ds2.getName()).isEqualTo("Untitled Datasource 2");
})
.verifyComplete();
}