Commit a32c9802 authored by Kertu Hiire's avatar Kertu Hiire Committed by GitHub

Merge pull request #11 from e-gov/release/v7.2

Release/v7.2
parents 703ba0ff 4028e173
......@@ -12,7 +12,7 @@
<parent>
<groupId>ee.ria.riha</groupId>
<artifactId>browser</artifactId>
<version>7.1.1</version>
<version>7.2-SNAPSHOT</version>
</parent>
<dependencies>
......@@ -39,11 +39,6 @@
<artifactId>jackson-databind</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
<dependency>
<groupId>com.github.fge</groupId>
<artifactId>json-schema-validator</artifactId>
......@@ -86,7 +81,7 @@
<dependency>
<groupId>ee.ria.riha</groupId>
<artifactId>storage-client</artifactId>
<version>0.6.0</version>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
......
......@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import ee.ria.riha.service.IllegalBrowserStateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
......@@ -70,9 +71,10 @@ public class RihaOrganizationAwareAuthenticationToken extends PreAuthenticatedAu
}
/**
* Either sets or clears active organization. Active organization must be one of the users organizationRoles.
* Either sets or clears active organization. Active organization must be one of the users organizationRoles or null
* to clear active organization
*
* @param organizationCode - new active organization
* @param organizationCode new active organization code or null
*/
public void setActiveOrganization(String organizationCode) {
if (log.isDebugEnabled()) {
......@@ -86,7 +88,8 @@ public class RihaOrganizationAwareAuthenticationToken extends PreAuthenticatedAu
this.activeOrganization = null;
} else {
if (!organizationsByCode.containsKey(organizationCode)) {
throw new IllegalArgumentException("User is not part of organization with code: " + organizationCode);
throw new IllegalBrowserStateException(
"User is not part of organization with code: " + organizationCode);
}
this.activeOrganization = organizationsByCode.get(organizationCode);
if (log.isDebugEnabled()) {
......
......@@ -70,7 +70,9 @@ public class ApplicationProperties {
private final CreatedInfoSystemsOverview createdInfoSystemsOverview = new CreatedInfoSystemsOverview();
private final NewIssue newIssue = new NewIssue();
private final NewIssueComment newIssueComment = new NewIssueComment();
private final IssueStatusUpdate issueStatusUpdate = new IssueStatusUpdate();
private String from;
private String recipientPattern;
}
@Getter
......@@ -119,5 +121,11 @@ public class ApplicationProperties {
private String hjsv;
}
@Getter
@Setter
public static class IssueStatusUpdate {
private boolean enabled;
}
}
......@@ -17,8 +17,8 @@ import org.springframework.web.client.RestTemplate;
public class StorageConfiguration {
@Bean
public StorageClient getStorageClient(RestTemplate restTemplate, ApplicationProperties applicationProperties) {
return new StorageClient(restTemplate, applicationProperties.getStorageClient().getBaseUrl());
public StorageClient getStorageClient(ApplicationProperties applicationProperties) {
return new StorageClient(applicationProperties.getStorageClient().getBaseUrl());
}
@Bean
......
......@@ -13,10 +13,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.*;
import java.util.Collections;
import java.util.List;
......@@ -46,6 +43,11 @@ public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
configurer.setUseSuffixPatternMatch(false);
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
@Override
public void addFormatters(FormatterRegistry registry) {
super.addFormatters(registry);
......
......@@ -6,7 +6,6 @@ import ee.ria.riha.storage.util.Pageable;
import ee.ria.riha.storage.util.PagedResponse;
import java.util.List;
import java.util.UUID;
/**
* Interface for repositories that persist InfoSystem entities.
......@@ -17,13 +16,11 @@ public interface InfoSystemRepository {
InfoSystem add(InfoSystem infoSystem);
InfoSystem load(String shortName);
InfoSystem load(String reference);
InfoSystem load(UUID uuid);
void update(String reference, InfoSystem infoSystem);
void update(String shortName, InfoSystem infoSystem);
void remove(String shortName);
void remove(String reference);
PagedResponse<InfoSystem> list(Pageable pageable, Filterable filterable);
......
package ee.ria.riha.domain;
import ee.ria.riha.domain.model.InfoSystem;
import ee.ria.riha.domain.model.IssueType;
import ee.ria.riha.service.ObjectNotFoundException;
import ee.ria.riha.storage.domain.MainResourceRepository;
import ee.ria.riha.storage.domain.model.MainResource;
import ee.ria.riha.storage.util.*;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
......@@ -20,18 +21,35 @@ public class RihaStorageInfoSystemRepository implements InfoSystemRepository {
private static final String NOT_IMPLEMENTED = "Not implemented";
private static final Pattern UUID_PATTERN = Pattern.compile(
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
private static final Function<MainResource, InfoSystem> MAIN_RESOURCE_TO_INFO_SYSTEM = mainResource -> {
if (mainResource == null) {
return null;
}
return new InfoSystem(mainResource.getJson_context());
InfoSystem infoSystem = new InfoSystem(mainResource.getJson_content());
infoSystem.setId(mainResource.getMain_resource_id());
infoSystem.setLastPositiveApprovalRequestType(mainResource.getLast_positive_approval_request_type() != null
? IssueType.valueOf(mainResource.getLast_positive_approval_request_type())
: null);
infoSystem.setLastPositiveApprovalRequestDate(mainResource.getLast_positive_approval_request_date());
infoSystem.setLastPositiveEstablishmentRequestDate(mainResource.getLast_positive_establishment_request_date());
infoSystem.setLastPositiveTakeIntoUseRequestDate(mainResource.getLast_positive_take_into_use_request_date());
infoSystem.setLastPositiveFinalizationRequestDate(mainResource.getLast_positive_finalization_request_date());
return infoSystem;
};
private static final Function<InfoSystem, MainResource> INFO_SYSTEM_TO_MAIN_RESOURCE = infoSystem -> {
if (infoSystem == null) {
return null;
}
return new MainResource(infoSystem.getJsonObject().toString());
MainResource mainResource = new MainResource();
mainResource.setJson_content(infoSystem.getJsonContent());
mainResource.setMain_resource_id(infoSystem.getId());
return mainResource;
};
private final MainResourceRepository mainResourceRepository;
......@@ -50,36 +68,52 @@ public class RihaStorageInfoSystemRepository implements InfoSystemRepository {
}
@Override
public InfoSystem load(String shortName) {
FilterRequest filter = new FilterRequest("short_name,=," + shortName, null, null);
List<InfoSystem> infoSystems = find(filter);
public InfoSystem load(String reference) {
if (isUuid(reference)) {
return loadByUuid(reference);
} else {
return loadByShortName(reference);
}
}
private boolean isUuid(String reference) {
return reference != null && UUID_PATTERN.matcher(reference).matches();
}
private InfoSystem loadByUuid(String uuid) {
List<InfoSystem> infoSystems = find(new FilterRequest("uuid,=," + uuid, null, null));
if (infoSystems.isEmpty()) {
throw new ObjectNotFoundException("Could not resolve info system by short name " + shortName);
throw new ObjectNotFoundException("error.storage.objectNotFound.infoSystem.byUuid", uuid);
}
return infoSystems.get(0);
}
@Override
public InfoSystem load(UUID uuid) {
FilterRequest filter = new FilterRequest("uuid,=," + uuid.toString(), null, null);
List<InfoSystem> infoSystems = find(filter);
private InfoSystem loadByShortName(String shortName) {
List<InfoSystem> infoSystems = find(new FilterRequest("short_name,=," + shortName, null, null));
if (infoSystems.isEmpty()) {
throw new ObjectNotFoundException("Could not resolve info system by uuid " + uuid.toString());
throw new ObjectNotFoundException("error.storage.objectNotFound.infoSystem.byShortName", shortName);
}
return infoSystems.get(0);
}
@Override
public void update(String shortName, InfoSystem infoSystem) {
public List<InfoSystem> find(Filterable filterable) {
List<MainResource> mainResources = mainResourceRepository.find(filterable);
return mainResources.stream()
.map(MAIN_RESOURCE_TO_INFO_SYSTEM)
.collect(Collectors.toList());
}
@Override
public void update(String reference, InfoSystem infoSystem) {
throw new RuntimeException(NOT_IMPLEMENTED);
}
@Override
public void remove(String shortName) {
public void remove(String reference) {
throw new RuntimeException(NOT_IMPLEMENTED);
}
......@@ -94,13 +128,4 @@ public class RihaStorageInfoSystemRepository implements InfoSystemRepository {
.map(MAIN_RESOURCE_TO_INFO_SYSTEM)
.collect(Collectors.toList()));
}
@Override
public List<InfoSystem> find(Filterable filterable) {
List<MainResource> mainResources = mainResourceRepository.find(filterable);
return mainResources.stream()
.map(MAIN_RESOURCE_TO_INFO_SYSTEM)
.collect(Collectors.toList());
}
}
......@@ -32,6 +32,7 @@ public class Issue implements IssueEntity {
private String organizationCode;
private IssueStatus status;
private IssueType type;
private IssueResolutionType resolutionType;
public Issue() {
}
......
......@@ -28,6 +28,7 @@ public class IssueEvent implements IssueEntity {
private String authorPersonalCode;
private String organizationName;
private String organizationCode;
private IssueResolutionType resolutionType;
@Override
public IssueEntityType getEntityType() {
......
......@@ -5,6 +5,14 @@ package ee.ria.riha.domain.model;
*/
public enum IssueEventType {
CLOSED;
/**
* Type of event that occurs when issue is closed
*/
CLOSED,
}
/**
* Type of event that occurs when resolution decision is made
*/
DECISION
}
\ No newline at end of file
package ee.ria.riha.domain.model;
/**
* Types of resolution for issue that implicitly request such resolution.
*
* @author Valentin Suhnjov
*/
public enum IssueResolutionType {
/**
* Indicates that issue was positively resolved
*/
POSITIVE,
/**
* Indicates that issue resolution was negative
*/
NEGATIVE,
/**
* Indicates that resolution will not be given
*/
DISMISSED
}
......@@ -8,11 +8,15 @@ package ee.ria.riha.domain.model;
public enum RelationType {
SUB_SYSTEM,
SUPER_SYSTEM;
SUPER_SYSTEM,
USED_SYSTEM,
USER_SYSTEM;
static {
SUB_SYSTEM.opposite = SUPER_SYSTEM;
SUPER_SYSTEM.opposite = SUB_SYSTEM;
USED_SYSTEM.opposite = USER_SYSTEM;
USER_SYSTEM.opposite = USED_SYSTEM;
}
private RelationType opposite;
......
package ee.ria.riha.domain.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
/**
* Extra special model of {@link ee.ria.riha.domain.model.Issue} that adds info system short name info.
*/
@Getter
@Setter
@Builder
@AllArgsConstructor
public class RihaIssueSummary {
private Long id;
private String infoSystemShortName;
private Date dateCreated;
private String title;
private String organizationName;
private String organizationCode;
private IssueStatus status;
}
package ee.ria.riha.service;
/**
* Base class for exceptions that contain error code and arguments. These exceptions, when presented to the caller,
* allow custom handling according to the code, type and arguments.
*
* @author Valentin Suhnjov
*/
public class CodedBrowserException extends BrowserException {
private final String code;
private final Object[] args;
public CodedBrowserException(String code) {
super(code);
this.code = code;
this.args = new Object[0];
}
public CodedBrowserException(String code, Throwable e) {
super(code, e);
this.code = code;
this.args = new Object[0];
}
public CodedBrowserException(String code, Object... args) {
super(code);
this.code = code;
this.args = args;
}
public CodedBrowserException(String code, Throwable e, Object... args) {
super(code, e);
this.code = code;
this.args = args;
}
public String getCode() {
return code;
}
public Object[] getArgs() {
return args;
}
}
......@@ -3,8 +3,8 @@ package ee.ria.riha.service;
import ee.ria.riha.conf.ApplicationProperties;
import ee.ria.riha.domain.InfoSystemRepository;
import ee.ria.riha.domain.model.InfoSystem;
import ee.ria.riha.service.notifications.model.InfoSystemDataModel;
import ee.ria.riha.service.notifications.model.NewInfoSystemsEmailNotification;
import ee.ria.riha.service.notification.model.InfoSystemDataModel;
import ee.ria.riha.service.notification.model.NewInfoSystemsEmailNotification;
import ee.ria.riha.storage.util.FilterRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
......
......@@ -10,13 +10,11 @@ import ee.ria.riha.storage.util.Filterable;
import ee.ria.riha.storage.util.Pageable;
import ee.ria.riha.storage.util.PagedResponse;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
......@@ -55,24 +53,23 @@ public class InfoSystemService {
public InfoSystem create(InfoSystem model) {
RihaOrganization organization = getActiveOrganization()
.orElseThrow(() -> new IllegalBrowserStateException("Unable to retrieve active organization"));
InfoSystem infoSystem = new InfoSystem(model.getJsonObject());
validateInfoSystemShortName(infoSystem.getShortName());
validateInfoSystemShortName(model.getShortName());
log.info("User '{}' with active organization '{}' is creating new info system with short name '{}'",
getRihaUserDetails().map(RihaUserDetails::getPersonalCode).orElse(NOT_SET_VALUE),
organization,
infoSystem.getShortName());
model.getShortName());
infoSystem.setUuid(UUID.randomUUID());
infoSystem.setOwnerCode(organization.getCode());
infoSystem.setOwnerName(organization.getName());
model.setUuid(UUID.randomUUID());
model.setOwnerCode(organization.getCode());
model.setOwnerName(organization.getName());
String creationTimestamp = isoDateTimeFormatter.format(ZonedDateTime.now());
infoSystem.setCreationTimestamp(creationTimestamp);
infoSystem.setUpdateTimestamp(creationTimestamp);
model.setCreationTimestamp(creationTimestamp);
model.setUpdateTimestamp(creationTimestamp);
infoSystemValidationService.validate(infoSystem.asJson());
infoSystemValidationService.validate(model.getJsonContent());
return infoSystemRepository.add(infoSystem);
return infoSystemRepository.add(model);
}
private void validateInfoSystemShortName(String shortName) {
......@@ -85,35 +82,45 @@ public class InfoSystemService {
}
/**
* Retrieves {@link InfoSystem} by its short name
* Retrieves {@link InfoSystem} by issue id
*
* @param uuid info system uuid
* @param issueId - issue id
* @return retrieved {@link InfoSystem}
*/
public InfoSystem get(UUID uuid) {
return infoSystemRepository.load(uuid);
public InfoSystem getByIssueId(Long issueId) {
Issue issue = issueService.getIssueById(issueId);
return get(issue.getInfoSystemUuid());
}
/**
* Retrieves {@link InfoSystem} by issue id
* Retrieves {@link InfoSystem} by its reference. Reference may be either info system UUID or short name.
*
* @param issueId - issue id
* @param reference info system reference
* @return retrieved {@link InfoSystem}
*/
public InfoSystem getByIssueId(Long issueId) {
Issue issue = issueService.getIssueById(issueId);
return get(issue.getInfoSystemUuid());
public InfoSystem get(String reference) {
return infoSystemRepository.load(reference);
}
/**
* Convenience method for retrieving {@link InfoSystem} by its UUID
*
* @param uuid info system uuid
* @return retrieved {@link InfoSystem}
*/
public InfoSystem get(UUID uuid) {
return get(uuid.toString());
}
/**
* Creates new record with the same UUID and owner. Other parts of {@link InfoSystem} are updated from model.
*
* @param shortName info system short name
* @param reference info system reference
* @param model updated {@link InfoSystem} model
* @return new {@link InfoSystem}
*/
public InfoSystem update(String shortName, InfoSystem model) {
InfoSystem existingInfoSystem = get(shortName);
public InfoSystem update(String reference, InfoSystem model) {
InfoSystem existingInfoSystem = get(reference);
log.info("User '{}' with active organization '{}'" +
" is updating info system with id {}, owner code '{}' and short name '{}'",
getRihaUserDetails().map(RihaUserDetails::getPersonalCode).orElse(NOT_SET_VALUE),
......@@ -122,48 +129,18 @@ public class InfoSystemService {
existingInfoSystem.getOwnerCode(),
existingInfoSystem.getShortName());
InfoSystem updatedInfoSystem = new InfoSystem(model.getJsonObject());
if (!shortName.equals(updatedInfoSystem.getShortName())) {
validateInfoSystemShortName(updatedInfoSystem.getShortName());
if (!existingInfoSystem.getShortName().equals(model.getShortName())) {
validateInfoSystemShortName(model.getShortName());
}
updatedInfoSystem.setUuid(existingInfoSystem.getUuid());
updatedInfoSystem.setOwnerCode(existingInfoSystem.getOwnerCode());
updatedInfoSystem.setOwnerName(existingInfoSystem.getOwnerName());
updatedInfoSystem.setCreationTimestamp(existingInfoSystem.getCreationTimestamp());
updatedInfoSystem.setUpdateTimestamp(isoDateTimeFormatter.format(ZonedDateTime.now()));
model.setUuid(existingInfoSystem.getUuid());
model.setOwnerCode(existingInfoSystem.getOwnerCode());
model.setOwnerName(existingInfoSystem.getOwnerName());
model.setCreationTimestamp(existingInfoSystem.getCreationTimestamp());
model.setUpdateTimestamp(isoDateTimeFormatter.format(ZonedDateTime.now()));
infoSystemValidationService.validate(updatedInfoSystem.asJson());
return infoSystemRepository.add(updatedInfoSystem);
}
infoSystemValidationService.validate(model.getJsonContent());
/**
* Extracts info system contacts from JSON.
*
* @param infoSystem - info system object
* @return list of info system contacts
*/
public List<String> getSystemContactsEmails(InfoSystem infoSystem) {
JSONArray jsonContactsArray = infoSystem.getJsonObject().optJSONArray("contacts");
List<String> contacts = new ArrayList<>();
if (jsonContactsArray == null || jsonContactsArray.length() == 0) {
return contacts;
}
for (int i = 0; i < jsonContactsArray.length(); i++) {
contacts.add(jsonContactsArray.optJSONObject(i).optString("email"));
}
return contacts;
}
/**
* Retrieves {@link InfoSystem} by its short name
*
* @param shortName info system short name
* @return retrieved {@link InfoSystem}
*/
public InfoSystem get(String shortName) {
return infoSystemRepository.load(shortName);
return infoSystemRepository.add(model);
}
@Autowired
......
......@@ -7,6 +7,7 @@ import ee.ria.riha.domain.model.IssueEntityType;
import ee.ria.riha.storage.domain.CommentRepository;
import ee.ria.riha.storage.domain.model.Comment;
import ee.ria.riha.storage.util.*;
import ee.ria.riha.web.model.IssueCommentModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -60,8 +61,8 @@ public class IssueCommentService {
return comment;
};