Commit 41a7b243 authored by Artem Hrybeniuk's avatar Artem Hrybeniuk
Browse files

Admin MVC to API

parent f808220c
/*
* Copyright 2022 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gringlobal.api.admin.v1;
import io.swagger.annotations.Api;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.blocks.security.service.PasswordPolicy;
import org.genesys.filerepository.persistence.ImageGalleryPersistence;
import org.genesys.filerepository.persistence.RepositoryFilePersistence;
import org.genesys.filerepository.persistence.RepositoryFolderRepository;
import org.gringlobal.api.v1.ApiBaseController;
import org.gringlobal.model.SysUser;
import org.gringlobal.persistence.InventoryMaintenancePolicyRepository;
import org.gringlobal.persistence.SiteRepository;
import org.gringlobal.persistence.kpi.ExecutionRepository;
import org.gringlobal.service.AccessionService;
import org.gringlobal.service.InventoryService;
import org.gringlobal.service.UserService;
import org.gringlobal.worker.GenesysDownloader;
import org.gringlobal.worker.UsdaGeographyUpdater;
import org.gringlobal.worker.UsdaTaxonomyUpdater;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController("adminApi1")
@PreAuthorize("hasAuthority('GROUP_ADMINS')")
@RequestMapping(AdminController.API_URL)
@Api(tags = { "adminv1" })
public class AdminController {
public static final Logger LOG = LoggerFactory.getLogger(AdminController.class);
/** The Constant API_URL. */
public static final String API_URL = ApiBaseController.APIv1_BASE + "/admin";
@Autowired(required = false)
private UsdaTaxonomyUpdater usdaTaxonomyUpdater;
@Autowired(required = false)
private UsdaGeographyUpdater usdaGeographyUpdater;
@Autowired(required = false)
private GenesysDownloader genesysDownloader;
@Autowired
private UserService userService;
@Autowired
private CustomAclService aclService;
@Autowired
private SiteRepository siteRepository;
@Autowired
private InventoryMaintenancePolicyRepository inventoryPolicyRepository;
@Autowired
private RepositoryFolderRepository folderRepository;
@Autowired
private RepositoryFilePersistence fileRepository;
@Autowired
private ImageGalleryPersistence imageGalleryRepository;
@Autowired
private ExecutionRepository kpiExecutionRepository;
@Autowired
private AccessionService accessionService;
@Autowired
private InventoryService inventoryService;
@PostMapping(path = "/taxonomy/update-usda")
public void updateUsdaTaxonomy() throws Exception {
LOG.info("Updating GRIN Taxonomy");
usdaTaxonomyUpdater.update();
LOG.info("Updating done");
}
@PostMapping(path = "/recalculate-accenumb")
public void recalculateAccessionNumbers() {
LOG.info("Recalculating accession numbers");
accessionService.recalculateAllAccessionNumbers();
}
@PostMapping(path = "/recalculate-inventorynumb")
public void recalculateInventoryNumbers() {
LOG.info("Recalculating inventory numbers");
inventoryService.recalculateAllInventoryNumbers();
}
@PostMapping(path = "/geo/update-usda")
public void updateUsdaGeography() throws Exception {
usdaGeographyUpdater.update();
}
@PostMapping(path = "/genesys/synchronize")
public void downloadFromGenesys(@RequestParam(name = "instituteCode") String instituteCode, @RequestParam(name="authorizationToken") String authorizationToken) throws Exception {
genesysDownloader.download(instituteCode, authorizationToken);
}
@PostMapping(path = "/add-user")
public void addUser(@RequestParam(name="username") String username, @RequestParam(name="password") String password) throws PasswordPolicy.PasswordPolicyException {
LOG.warn("Adding user {}", username);
SysUser user = new SysUser();
user.setUsername(username);
user.setPassword(password);
user = userService.create(user);
LOG.warn("Added user id={} username={}", user.getId(), user.getUsername());
}
@PostMapping(value = "/site/fix-acl")
@Transactional
public void aclFixSiteAcl() {
LOG.warn("Adding ACL for Sites");
siteRepository.findAll().forEach(site -> aclService.createOrUpdatePermissions(site));
}
@PostMapping(value = "/inventory/fix-acl")
@Transactional
public void aclFixInventoryPolicyAcl() {
LOG.warn("Adding ACL for InventoryMaintenancePolicies");
inventoryPolicyRepository.findAll().forEach(policy -> aclService.createOrUpdatePermissions(policy));
}
@PostMapping(value = "/repository/fix-acl")
@Transactional
public void aclFixRepositoryAcl() {
LOG.warn("Adding ACL for Repository folders");
folderRepository.findAll().forEach(folder -> aclService.createOrUpdatePermissions(folder));
LOG.warn("Adding ACL for Repository files");
fileRepository.findAll().forEach(file -> aclService.createOrUpdatePermissions(file));
LOG.warn("Adding ACL for Image galleries");
imageGalleryRepository.findAll().forEach(gallery -> aclService.createOrUpdatePermissions(gallery));
}
@PostMapping(value = "/kpi/acl")
@Transactional
public void aclFixKPIAcl() {
LOG.warn("Adding ACL support to KPI Execution");
kpiExecutionRepository.findAll().forEach(execution -> {
LOG.warn("Making KPI Execution {} ACL-ready", execution.getName());
aclService.createOrUpdatePermissions(execution);
});
}
}
/*
* Copyright 2022 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gringlobal.api.admin.v1;
import com.hazelcast.core.DistributedObject;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.map.LocalMapStats;
import io.swagger.annotations.Api;
import org.gringlobal.api.v1.ApiBaseController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController("cacheApi1")
@PreAuthorize("hasAuthority('GROUP_ADMINS')")
@RequestMapping(CacheController.API_URL)
@Api(tags = { "cachev1" })
public class CacheController {
/** The Constant API_URL. */
public static final String API_URL = ApiBaseController.APIv1_BASE + "/admin/cache";
public static final Logger LOG = LoggerFactory.getLogger(CacheController.class);
@Autowired
private CacheManager cacheManager;
@PostMapping(value = "/clearCache/clearAll")
public void clearCacheAll() {
for (String cacheName : cacheManager.getCacheNames()) {
clearCache(cacheName);
}
}
@PostMapping(value = "/clearCaches")
public void clearCaches(@RequestBody final List<String> cacheNames) {
for (String cacheName: cacheNames) {
clearCache(cacheName);
}
}
@PostMapping(value = "/clearCache/{name}")
public void clearCache(@PathVariable("name") String cacheName) {
final Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
LOG.info("Clearing cache {}", cacheName);
cache.clear();
} else {
LOG.info("No such cache: {}", cacheName);
}
}
@GetMapping(value = "", produces = { MediaType.APPLICATION_JSON_VALUE })
public CacheStatsResponse cacheStats() {
List<CacheStats> cacheMaps = new ArrayList<>();
List<Object> cacheOther = new ArrayList<>();
Set<HazelcastInstance> instances = Hazelcast.getAllHazelcastInstances();
for (HazelcastInstance hz : instances) {
if (LOG.isDebugEnabled())
LOG.debug("\n\nCache stats Instance: {}", hz.getName());
for (DistributedObject o : hz.getDistributedObjects()) {
if (o instanceof IMap) {
IMap<?, ?> imap = (IMap<?, ?>) o;
cacheMaps.add(new CacheStats(imap));
if (LOG.isDebugEnabled()) {
LOG.debug("{}: {} {}", imap.getServiceName(), imap.getName(), imap.getPartitionKey());
LocalMapStats localMapStats = imap.getLocalMapStats();
LOG.debug("created: {}", localMapStats.getCreationTime());
LOG.debug("owned entries: {}", localMapStats.getOwnedEntryCount());
LOG.debug("backup entries: {}", localMapStats.getBackupEntryCount());
LOG.debug("locked entries: {}", localMapStats.getLockedEntryCount());
LOG.debug("dirty entries: {}", localMapStats.getDirtyEntryCount());
LOG.debug("hits: {}", localMapStats.getHits());
LOG.debug("puts: {}", localMapStats.getPutOperationCount());
LOG.debug("last update: {}", localMapStats.getLastUpdateTime());
LOG.debug("last access: {}", localMapStats.getLastAccessTime());
}
} else {
if (LOG.isDebugEnabled())
LOG.debug("{} {}", o.getClass(), o);
cacheOther.add(o);
}
}
}
return new CacheStatsResponse(cacheMaps, cacheOther);
}
public static class CacheStatsResponse {
public List<CacheStats> cacheMaps;
public List<String> cacheOther;
public CacheStatsResponse(List<CacheStats> cacheMaps, List<Object> cacheOther) {
this.cacheMaps = cacheMaps;
this.cacheOther = cacheOther.stream().map(Object::toString).collect(Collectors.toList());
}
}
public static class CacheStats {
public String serviceName;
public String name;
public String statsOwnedEntryCount;
public String statsLockedEntryCount;
public String statsPutOperationCount;
public String statsHits;
public CacheStats(IMap<?, ?> imap) {
this.serviceName = imap.getServiceName();
this.name = imap.getName();
LocalMapStats mapStats = imap.getLocalMapStats();
statsOwnedEntryCount = String.valueOf(mapStats.getOwnedEntryCount());
statsLockedEntryCount = String.valueOf(mapStats.getLockedEntryCount());
statsPutOperationCount = String.valueOf(mapStats.getPutOperationCount());
statsHits = String.valueOf(mapStats.getHits());
}
}
}
/*
* Copyright 2022 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gringlobal.api.admin.v1;
import io.swagger.annotations.Api;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.gringlobal.api.v1.ApiBaseController;
import org.gringlobal.component.elastic.ElasticReindex;
import org.gringlobal.service.ElasticsearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
@RestController("elasticSearchApi1")
@PreAuthorize("hasAuthority('GROUP_ADMINS')")
@RequestMapping(ElasticsearchController.API_URL)
@Api(tags = { "elasticv1" })
public class ElasticsearchController {
/** The Constant API_URL. */
public static final String API_URL = ApiBaseController.APIv1_BASE + "/admin/elastic";
public static final Logger LOG = LoggerFactory.getLogger(ElasticsearchController.class);
@Autowired(required = false)
private ElasticsearchService elasticsearchService;
@Resource
private BlockingQueue<ElasticReindex> elasticReindexQueue;
@Autowired
private TaskExecutor taskExecutor;
@GetMapping(value = "", produces = { MediaType.APPLICATION_JSON_VALUE })
public IndexResponse getIndexResponse() throws IOException {
IndexResponse indexResponse = new IndexResponse();
indexResponse.indexes = elasticsearchService.listIndices().getAliases();
indexResponse.reindexTypes = createReindexTypesMap();
indexResponse.updateQueueSize = elasticReindexQueue.size();
return indexResponse;
}
@PostMapping(value = "/reindex")
public void reindexElasticContent(@RequestParam(value = "type") String type) {
if (elasticReindexQueue.size() > 0) {
throw new RuntimeException("Reindex queue not empty or operation is locked! Unable to run new indexing.");
}
if (type.equals("All")) {
taskExecutor.execute(() -> {
try {
LOG.warn("Reindexing EVERYTHING!!!");
elasticsearchService.reindexAll();
} catch (Throwable e) {
LOG.error("Error executing reindexAll", e);
}
});
} else {
taskExecutor.execute(() -> {
try {
LOG.warn("Reindexing {}", type);
elasticsearchService.reindex(Class.forName(type));
} catch (Throwable e) {
LOG.error("Error executing reindex of " + type, e);
}
});
}
}
@PostMapping(value = "/realias")
public void moveAlias(@RequestParam(name = "aliasName") String aliasName, @RequestParam(name = "indexName") String indexName) {
elasticsearchService.realias(aliasName, indexName);
}
@PostMapping(value = "/delete-alias/{name}")
public void deleteAlias(@PathVariable(name = "name") String aliasName) {
elasticsearchService.deleteAlias(aliasName);
}
@PostMapping(value = "/delete-index/{name}")
public void deleteIndex(@PathVariable(name = "name") String indexName) {
elasticsearchService.deleteIndex(indexName);
}
public static class IndexResponse {
public Map<String, List<AliasMetaData>> indexes;
public Map<String, String> reindexTypes;
public int updateQueueSize;
}
private Map<String, String> createReindexTypesMap() {
Map<String, String> reindexTypesMap = new HashMap<>();
for (Class<?> clazz : elasticsearchService.getIndexedEntities()) {
reindexTypesMap.put(clazz.getSimpleName(), clazz.getName());
}
return reindexTypesMap;
}
}
/*
* Copyright 2022 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gringlobal.api.admin.v1;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.gringlobal.api.v1.ApiBaseController;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController("loggerAPIV1")
@RequestMapping(LoggerController.API_URL)
@PreAuthorize("hasAuthority('GROUP_ADMINS')")
@Api(tags = { "loggerv1" })
public class LoggerController {
/** The Constant API_URL. */
public static final String API_URL = ApiBaseController.APIv1_BASE + "/admin/logger";
@GetMapping(value = "", produces = { MediaType.APPLICATION_JSON_VALUE })
public List<LoggerResponse> getLoggers() {
return getAllLoggers();
}
@GetMapping(value = "/{loggerName}", produces = { MediaType.APPLICATION_JSON_VALUE })
public LoggerResponse getLogger(@PathVariable(value = "loggerName") String loggerName) {
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Logger logger;
if ("root".equalsIgnoreCase(loggerName))
logger = context.getRootLogger();
else
logger = context.getLogger(loggerName);
List<String> appenders = new ArrayList<>(context.getConfiguration().getAppenders().keySet());
return new LoggerResponse(logger.getName(), logger.getLevel().toString(), appenders);
}
@PostMapping(value = "{loggerName}/changeLoger/{loggerLevel}", produces = { MediaType.APPLICATION_JSON_VALUE })
public LoggerResponse changeLogger(@PathVariable(value = "loggerLevel") String loggerLevel, @PathVariable(value = "loggerName") String loggerName) {
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
LoggerConfig loggerConfig;
if (loggerName == null || "root".equalsIgnoreCase(loggerName)) {
loggerConfig = config.getRootLogger();
} else {
loggerConfig = config.getLoggerConfig(loggerName);
}
if (loggerConfig != null) {
loggerConfig.setLevel(loggerLevel == null ? null : Level.toLevel(loggerLevel));
context.updateLoggers();
List<String> appenders = new ArrayList<>(context.getConfiguration().getAppenders().keySet());
return new LoggerResponse(loggerConfig.getName(), loggerConfig.getLevel().toString(), appenders);
} else {
return null;
}
}
private List<LoggerResponse> getAllLoggers() {
List<LoggerResponse> loggers = new ArrayList<>();
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
Configuration config = loggerContext.getConfiguration();
List<String> appenders = new ArrayList<>(loggerContext.getConfiguration().getAppenders().keySet());
config.getLoggers().values().forEach(loggerConfig -> {
LoggerResponse loggerResponse = new LoggerResponse();
loggerResponse.loggerName = loggerConfig.getName();
loggerResponse.loggerLevel = loggerConfig.getLevel().toString();
loggerResponse.appenders = appenders;
loggers.add(loggerResponse);
});
loggers.sort((o1, o2) -> {
// root logger is on the top
if (StringUtils.isEmpty(o1.loggerName)) {
return -1;
} else if (StringUtils.isEmpty(o1.loggerName)) {
return 1;
}
// otherwise sort by name
return o1.loggerName.compareTo(o2.loggerName);
});
return loggers;
}
@PostMapping(value = "/addLoger", produces = { MediaType.APPLICATION_JSON_VALUE })
public LoggerResponse addLogger(@RequestParam(value = "nameNewLogger") String nameNewLogger, @RequestParam("loggerLevel") String loggerLevel) {
LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
Configuration config = loggerContext.getConfiguration();
Level level = Level.toLevel(loggerLevel);