Skip to content
Snippets Groups Projects
Commit c8b0ad8a authored by Lukasz Lopatowski's avatar Lukasz Lopatowski
Browse files

Merge remote-tracking branch 'origin/develop' into develop

parents f38bf380 5b0a7e7d
No related branches found
No related tags found
1 merge request!273Release 1.8.0 update
Pipeline #95364 passed
......@@ -11,8 +11,10 @@ import net.geant.nmaas.portal.persistent.entity.Role;
import net.geant.nmaas.portal.persistent.entity.User;
import net.geant.nmaas.portal.persistent.repositories.ContentRepository;
import net.geant.nmaas.portal.persistent.repositories.UserRepository;
import net.geant.nmaas.portal.service.ApplicationService;
import net.geant.nmaas.portal.service.ConfigurationManager;
import net.geant.nmaas.portal.service.DomainService;
import net.geant.nmaas.portal.service.impl.FormioSanitizerService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
......@@ -157,6 +159,9 @@ public class PortalConfig {
@Autowired
private ConfigurationManager configurationManager;
@Autowired
private ApplicationService applicationService;
@Override
public void afterPropertiesSet() {
ConfigurationView configurationView = ConfigurationView.builder()
......@@ -179,6 +184,8 @@ public class PortalConfig {
} catch (OnlyOneConfigurationSupportedException e) {
log.debug("Portal configuration already exists. Skipping initialization.");
}
applicationService.checkAllFormioTemplate();
}
};
}
......
......@@ -326,6 +326,7 @@ public class ApplicationController extends AppBaseController {
application.setCreationDate(LocalDateTime.now());
this.applicationService.setMissingProperties(application, appId);
ApplicationServiceImpl.clearIds(application);
this.applicationService.checkAndUpdateFormioTemplate(application);
this.applicationService.update(application);
// create, add and persist new application version
......
......@@ -36,4 +36,8 @@ public interface ApplicationService {
boolean exists(String name, String version);
Application checkAndUpdateFormioTemplate(Application application);
void checkAllFormioTemplate();
}
......@@ -2,6 +2,7 @@ package net.geant.nmaas.portal.service.impl;
import freemarker.template.Configuration;
import freemarker.template.Template;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.nmservice.configuration.entities.ConfigFileTemplate;
......@@ -21,7 +22,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import jakarta.transaction.Transactional;
import java.io.IOException;
import java.time.OffsetDateTime;
import java.util.Comparator;
......@@ -39,186 +39,210 @@ import static net.geant.nmaas.portal.events.ApplicationListUpdatedEvent.Applicat
@Slf4j
public class ApplicationServiceImpl implements ApplicationService {
private final ApplicationRepository applicationRepository;
private final ApplicationEventPublisher eventPublisher;
@Override
@Transactional
@CachePut("applicationBaseS")
public Application update(Application application) {
checkApp(application);
Application saved = applicationRepository.save(application);
generateApplicationListUpdatedEvent(saved, UPDATED);
return saved;
}
private void generateApplicationListUpdatedEvent(Application app, ApplicationAction action) {
ApplicationListUpdatedEvent event = new ApplicationListUpdatedEvent(
ApplicationServiceImpl.class,
app.getName(),
app.getVersion(),
action,
app.getAppDeploymentSpec()
);
this.eventPublisher.publishEvent(event);
}
@Override
public boolean exists(String name, String version) {
return applicationRepository.existsByNameAndVersion(name, version);
}
@Override
@CachePut("applicationBaseS")
public Application create(Application application) {
if (application.getId() != null) {
throw new ProcessingException("While creating id must be null");
}
clearIds(application);
Application saved = applicationRepository.save(application);
generateApplicationListUpdatedEvent(saved, ADDED);
return saved;
}
@Override
@CacheEvict(value = "applicationBaseS")
public void delete(Long id) {
checkParam(id);
applicationRepository.findById(id).ifPresent(app -> {
if(app.getState().isChangeAllowed(ApplicationState.DELETED)) {
app.setState(ApplicationState.DELETED);
applicationRepository.save(app);
generateApplicationListUpdatedEvent(app, DELETED);
}
});
}
@Override
public Optional<Application> findApplication(Long id) {
if (id != null) {
return applicationRepository.findById(id);
} else {
throw new IllegalArgumentException("applicationId is null");
}
}
@Override
public Optional<Application> findApplication(String name, String version) {
return this.applicationRepository.findByNameAndVersion(name, version);
}
@Override
public Application findApplicationLatestVersion(String name) {
if (!StringUtils.hasText(name)) {
throw new IllegalArgumentException("Application name cannot be null or empty");
}
return applicationRepository.findByName(name).stream()
.max(Comparator.comparing(Application::getCreationDate))
.orElseThrow(() -> new MissingElementException("Application " + name + " cannot be found"));
}
@Override
public Page<Application> findAll(Pageable pageable) {
return applicationRepository.findAll(pageable);
}
@Override
public List<Application> findAll() {
return applicationRepository.findAll();
}
@Override
@CacheEvict(value = "applicationBaseS", allEntries = true)
public void changeApplicationState(Application app, ApplicationState state) {
if (!app.getState().isChangeAllowed(state)) {
throw new IllegalStateException("Application state transition from " + app.getState() + " to " + state + " is not allowed.");
}
if (state.equals(ApplicationState.ACTIVE)) {
checkApp(app);
checkTemplates(app);
}
app.setState(state);
if (state.equals(ApplicationState.DELETED)) {
String suffix = "_DELETED_" + OffsetDateTime.now();
app.setName(app.getName() + suffix);
}
applicationRepository.save(app);
}
private void checkApp(Application app) {
if(app == null) {
throw new IllegalArgumentException("App cannot be null");
}
app.validate();
app.getAppDeploymentSpec().validate();
app.getAppDeploymentSpec().getKubernetesTemplate().validate();
checkTemplates(app);
}
private void checkTemplates(Application app) {
if (app.getAppConfigurationSpec().isConfigFileRepositoryRequired()) {
app.getAppConfigurationSpec().getTemplates().forEach(this::validateConfigFileTemplates);
}
}
private void validateConfigFileTemplates(ConfigFileTemplate configFileTemplate) {
try {
new Template("test", configFileTemplate.getConfigFileTemplateContent(), new Configuration(Configuration.VERSION_2_3_28));
} catch (IOException e) {
throw new IllegalArgumentException("Template " + configFileTemplate.getConfigFileName() + " is invalid");
}
}
@Override
public void setMissingProperties(Application app, Long appId) {
this.setMissingTemplatesId(app, appId);
}
private void checkParam(Long id) {
if(id == null) {
throw new IllegalArgumentException("id is null");
}
}
private void setMissingTemplatesId(Application app, Long appId) {
app.getAppConfigurationSpec().getTemplates()
.forEach(template -> template.setApplicationId(appId));
}
public static void clearIds(Application app) {
if (app.getConfigWizardTemplate() != null) {
app.getConfigWizardTemplate().setId(null);
}
if (app.getConfigUpdateWizardTemplate() != null) {
app.getConfigUpdateWizardTemplate().setId(null);
}
if (app.getAppConfigurationSpec() != null) {
app.getAppConfigurationSpec().setId(null);
app.getAppConfigurationSpec().getTemplates().forEach(a -> a.setId(null));
}
if (app.getAppDeploymentSpec() != null) {
app.getAppDeploymentSpec().setId(null);
app.getAppDeploymentSpec().getAccessMethods().forEach(a -> a.setId(null));
app.getAppDeploymentSpec().getStorageVolumes().forEach(a -> a.setId(null));
app.getAppDeploymentSpec().getKubernetesTemplate().setId(null);
app.getAppDeploymentSpec().getKubernetesTemplate().getChart().setId(null);
}
}
@Override
public Map<String, Long> findAllActiveVersionNumbers(String name) {
if (!StringUtils.hasText(name)) {
throw new IllegalArgumentException("Application name cannot be null or empty");
}
Map<String, Long> versions = new HashMap<>();
applicationRepository.findByName(name).stream()
.filter(app -> ApplicationState.ACTIVE.equals(app.getState()))
.forEach(app ->
versions.put(
app.getAppDeploymentSpec().getKubernetesTemplate().getChart().getVersion(),
app.getId())
);
return versions;
}
private final ApplicationRepository applicationRepository;
private final ApplicationEventPublisher eventPublisher;
private final FormioSanitizerService formioSanitizerService;
@Override
@Transactional
@CachePut("applicationBaseS")
public Application update(Application application) {
checkApp(application);
checkAndUpdateFormioTemplate(application);
Application saved = applicationRepository.save(application);
generateApplicationListUpdatedEvent(saved, UPDATED);
return saved;
}
private void generateApplicationListUpdatedEvent(Application app, ApplicationAction action) {
ApplicationListUpdatedEvent event = new ApplicationListUpdatedEvent(
ApplicationServiceImpl.class,
app.getName(),
app.getVersion(),
action,
app.getAppDeploymentSpec()
);
this.eventPublisher.publishEvent(event);
}
@Override
public boolean exists(String name, String version) {
return applicationRepository.existsByNameAndVersion(name, version);
}
@Override
@CachePut("applicationBaseS")
public Application create(Application application) {
if (application.getId() != null) {
throw new ProcessingException("While creating id must be null");
}
clearIds(application);
Application saved = applicationRepository.save(application);
generateApplicationListUpdatedEvent(saved, ADDED);
return saved;
}
@Override
@CacheEvict(value = "applicationBaseS")
public void delete(Long id) {
checkParam(id);
applicationRepository.findById(id).ifPresent(app -> {
if (app.getState().isChangeAllowed(ApplicationState.DELETED)) {
app.setState(ApplicationState.DELETED);
applicationRepository.save(app);
generateApplicationListUpdatedEvent(app, DELETED);
}
});
}
@Override
public Optional<Application> findApplication(Long id) {
if (id != null) {
return applicationRepository.findById(id);
} else {
throw new IllegalArgumentException("applicationId is null");
}
}
@Override
public Optional<Application> findApplication(String name, String version) {
return this.applicationRepository.findByNameAndVersion(name, version);
}
@Override
public Application findApplicationLatestVersion(String name) {
if (!StringUtils.hasText(name)) {
throw new IllegalArgumentException("Application name cannot be null or empty");
}
return applicationRepository.findByName(name).stream()
.max(Comparator.comparing(Application::getCreationDate))
.orElseThrow(() -> new MissingElementException("Application " + name + " cannot be found"));
}
@Override
public Page<Application> findAll(Pageable pageable) {
return applicationRepository.findAll(pageable);
}
@Override
public List<Application> findAll() {
return applicationRepository.findAll();
}
@Override
@CacheEvict(value = "applicationBaseS", allEntries = true)
public void changeApplicationState(Application app, ApplicationState state) {
if (!app.getState().isChangeAllowed(state)) {
throw new IllegalStateException("Application state transition from " + app.getState() + " to " + state + " is not allowed.");
}
if (state.equals(ApplicationState.ACTIVE)) {
checkApp(app);
checkTemplates(app);
}
app.setState(state);
if (state.equals(ApplicationState.DELETED)) {
String suffix = "_DELETED_" + OffsetDateTime.now();
app.setName(app.getName() + suffix);
}
applicationRepository.save(app);
}
private void checkApp(Application app) {
if (app == null) {
throw new IllegalArgumentException("App cannot be null");
}
app.validate();
app.getAppDeploymentSpec().validate();
app.getAppDeploymentSpec().getKubernetesTemplate().validate();
checkTemplates(app);
}
private void checkTemplates(Application app) {
if (app.getAppConfigurationSpec().isConfigFileRepositoryRequired()) {
app.getAppConfigurationSpec().getTemplates().forEach(this::validateConfigFileTemplates);
}
}
private void validateConfigFileTemplates(ConfigFileTemplate configFileTemplate) {
try {
new Template("test", configFileTemplate.getConfigFileTemplateContent(), new Configuration(Configuration.VERSION_2_3_28));
} catch (IOException e) {
throw new IllegalArgumentException("Template " + configFileTemplate.getConfigFileName() + " is invalid");
}
}
@Override
public void setMissingProperties(Application app, Long appId) {
this.setMissingTemplatesId(app, appId);
}
private void checkParam(Long id) {
if (id == null) {
throw new IllegalArgumentException("id is null");
}
}
private void setMissingTemplatesId(Application app, Long appId) {
app.getAppConfigurationSpec().getTemplates()
.forEach(template -> template.setApplicationId(appId));
}
public static void clearIds(Application app) {
if (app.getConfigWizardTemplate() != null) {
app.getConfigWizardTemplate().setId(null);
}
if (app.getConfigUpdateWizardTemplate() != null) {
app.getConfigUpdateWizardTemplate().setId(null);
}
if (app.getAppConfigurationSpec() != null) {
app.getAppConfigurationSpec().setId(null);
app.getAppConfigurationSpec().getTemplates().forEach(a -> a.setId(null));
}
if (app.getAppDeploymentSpec() != null) {
app.getAppDeploymentSpec().setId(null);
app.getAppDeploymentSpec().getAccessMethods().forEach(a -> a.setId(null));
app.getAppDeploymentSpec().getStorageVolumes().forEach(a -> a.setId(null));
app.getAppDeploymentSpec().getKubernetesTemplate().setId(null);
app.getAppDeploymentSpec().getKubernetesTemplate().getChart().setId(null);
}
}
@Override
public Map<String, Long> findAllActiveVersionNumbers(String name) {
if (!StringUtils.hasText(name)) {
throw new IllegalArgumentException("Application name cannot be null or empty");
}
Map<String, Long> versions = new HashMap<>();
applicationRepository.findByName(name).stream()
.filter(app -> ApplicationState.ACTIVE.equals(app.getState()))
.forEach(app ->
versions.put(
app.getAppDeploymentSpec().getKubernetesTemplate().getChart().getVersion(),
app.getId())
);
return versions;
}
@Override
public Application checkAndUpdateFormioTemplate(Application application) {
application.getConfigWizardTemplate().setTemplate(formioSanitizerService.sanitizeFormioJson(application.getConfigWizardTemplate().getTemplate()));
return application;
}
@Override
public void checkAllFormioTemplate() {
log.warn("Checking formio template for # in keys ");
this.findAll().forEach(app -> {
log.warn("Sanitize formio wizards keys for app {}", app.getId());
if(app.getConfigWizardTemplate() != null) {
app.getConfigWizardTemplate().setTemplate(formioSanitizerService.sanitizeFormioJson(app.getConfigWizardTemplate().getTemplate()));
log.warn("sanitezed done", app.getConfigWizardTemplate().getTemplate());
}
if(app.getConfigUpdateWizardTemplate() != null) {
app.getConfigUpdateWizardTemplate().setTemplate(formioSanitizerService.sanitizeFormioJson(app.getConfigUpdateWizardTemplate().getTemplate()));
}
applicationRepository.save(app);
});
}
}
package net.geant.nmaas.portal.service.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.stereotype.Service;
@Service
public class FormioSanitizerService {
private final ObjectMapper objectMapper;
public FormioSanitizerService(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public String sanitizeFormioJson(String json) {
try {
JsonNode root = objectMapper.readTree(json);
sanitizeKeysRecursively(root);
return objectMapper.writeValueAsString(root);
} catch (Exception e) {
throw new RuntimeException("Error parsing formio", e);
}
}
/**
* Iterate over JSON and change # to _dot_ in key fields
*/
private void sanitizeKeysRecursively(JsonNode node) {
if (node.isObject()) {
ObjectNode objNode = (ObjectNode) node;
if (objNode.has("key")) {
String key = objNode.get("key").asText();
if (key.contains("#")) {
objNode.put("key", key.replace("#", "_dot_"));
}
}
objNode.fields().forEachRemaining(entry -> {
sanitizeKeysRecursively(entry.getValue());
});
} else if (node.isArray()) {
for (JsonNode item : node) {
sanitizeKeysRecursively(item);
}
}
}
}
......@@ -41,12 +41,13 @@ public class ApplicationServiceImplTest {
ApplicationRepository applicationRepository = mock(ApplicationRepository.class);
ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);
FormioSanitizerService formioSanitizerService = mock(FormioSanitizerService.class);
ApplicationServiceImpl applicationService;
@BeforeEach
void setup(){
applicationService = new ApplicationServiceImpl(applicationRepository, eventPublisher);
applicationService = new ApplicationServiceImpl(applicationRepository, eventPublisher, formioSanitizerService);
}
@Test
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment