Skip to content
Snippets Groups Projects
Commit 0979b058 authored by OZGCloud's avatar OZGCloud
Browse files

Merge pull request 'OZG-2737_MigrationUserId' (#61) from OZG-2737_MigrationUserId into master

parents 11affa4b 672df748
Branches
Tags
No related merge requests found
package de.itvsh.ozg.pluto.common.migration;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.bson.Document;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import io.mongock.api.annotations.ChangeUnit;
import io.mongock.api.annotations.Execution;
import io.mongock.api.annotations.RollbackExecution;
import lombok.extern.log4j.Log4j2;
@Log4j2
@ChangeUnit(id = "2022-09-06 10:45:00 OZG-2737", order = "M012", author = "maku", runAlways = true)
public class M012_MigrationUserId {// NOSONAR
private static final String KOP_USERMANAGER_URL_KEY = "kop.usermanager.url";
static final String COLLECTION_VORGANG_NAME = "vorgang";
static final String ASSIGNED_TO_FIELD = "assignedTo";
static final String COLLECTION_COMMAND_NAME = "command";
static final String CREATED_BY_FIELD = "createdBy";
static final String COLLECTION_VORGANG_ATTACHED_ITEM_NAME = "vorgangAttachedItem";
static final String VORGANG_ATTACHED_ITEM_ITEM_FIELD = "item";
static final String VORGANG_ATTACHED_ITEM_CREATED_BY_FIELD = VORGANG_ATTACHED_ITEM_ITEM_FIELD + "." + CREATED_BY_FIELD;
static final Pattern UUID_PATTERN = Pattern.compile("-");
private MongoTemplate template;
private RestTemplate restTemplate;
private String userManagerUrlTemplate;
@Execution
public void doMigration(MongoTemplate template, RestTemplate restTemplate, Environment environment) {
var userManagerUrl = environment.getProperty(KOP_USERMANAGER_URL_KEY);
if (Objects.nonNull(userManagerUrl)) {
this.template = template;
this.restTemplate = restTemplate;
this.userManagerUrlTemplate = userManagerUrl + "/%s";
migrate();
} else {
logUrlIsNotConfigured();
}
}
private void migrate() {
migrateAssignedTo();
migrateCommandCreatedBy();
migrateVorgangAttachedItemCreatedBy();
}
void migrateAssignedTo() {
findVorgangWithFilledAssignedTo().forEach(this::updateAssignedTo);
}
private Stream<Document> findVorgangWithFilledAssignedTo() {
return template.stream(createFindVorgangWithFilledAssignedToQuery(), Document.class, COLLECTION_VORGANG_NAME).stream();
}
Query createFindVorgangWithFilledAssignedToQuery() {
return Query.query(Criteria.where(ASSIGNED_TO_FIELD).exists(Boolean.TRUE)
.andOperator(Criteria.where(ASSIGNED_TO_FIELD).regex(UUID_PATTERN)));
}
private void updateAssignedTo(Document vorgang) {
getNewInternalId(vorgang.getString(ASSIGNED_TO_FIELD)).ifPresent(newId -> doUpdateAssignedTo(vorgang, newId));
}
private void doUpdateAssignedTo(Document vorgang, String newId) {
vorgang.put(ASSIGNED_TO_FIELD, newId);
template.save(vorgang, COLLECTION_VORGANG_NAME);
}
void migrateCommandCreatedBy() {
findCommandWithCreateBy().forEach(this::updateCreatedBy);
}
private Stream<Document> findCommandWithCreateBy() {
return template.stream(createFindCommandWithCreatedByQuery(), Document.class, COLLECTION_COMMAND_NAME).stream();
}
Query createFindCommandWithCreatedByQuery() {
return Query.query(Criteria.where(CREATED_BY_FIELD).exists(Boolean.TRUE)
.andOperator(Criteria.where(CREATED_BY_FIELD).regex(UUID_PATTERN)));
}
private void updateCreatedBy(Document command) {
getNewInternalId(command.getString(CREATED_BY_FIELD)).ifPresent(newId -> doUpdateCreatedBy(command, newId));
}
private void doUpdateCreatedBy(Document command, String newId) {
command.put(CREATED_BY_FIELD, newId);
template.save(command, COLLECTION_COMMAND_NAME);
}
void migrateVorgangAttachedItemCreatedBy() {
findVorgangAttachedItemWithCreatedBy().forEach(this::updateVorgangAttachedItemCreatedBy);
}
private Stream<Document> findVorgangAttachedItemWithCreatedBy() {
return template.stream(createFindVorgangAttachedItemdWithCreatedByQuery(), Document.class, COLLECTION_VORGANG_ATTACHED_ITEM_NAME).stream();
}
Query createFindVorgangAttachedItemdWithCreatedByQuery() {
return Query.query(Criteria.where(VORGANG_ATTACHED_ITEM_CREATED_BY_FIELD).exists(Boolean.TRUE)
.andOperator(Criteria.where(VORGANG_ATTACHED_ITEM_CREATED_BY_FIELD).regex(UUID_PATTERN)));
}
private void updateVorgangAttachedItemCreatedBy(Document vorgangAttachedItem) {
var itemCreatedBy = ((Document) vorgangAttachedItem.get(VORGANG_ATTACHED_ITEM_ITEM_FIELD)).getString(CREATED_BY_FIELD);
getNewInternalId(itemCreatedBy).ifPresent(newId -> doUpdateVorgangAttachedItemCreatedBy(vorgangAttachedItem, newId));
}
private void doUpdateVorgangAttachedItemCreatedBy(Document vorgangAttachedItem, String newId) {
var item = (Document) vorgangAttachedItem.get(VORGANG_ATTACHED_ITEM_ITEM_FIELD);
item.put(CREATED_BY_FIELD, newId);
vorgangAttachedItem.put(VORGANG_ATTACHED_ITEM_ITEM_FIELD, item);
template.save(vorgangAttachedItem, COLLECTION_VORGANG_ATTACHED_ITEM_NAME);
}
private Optional<String> getNewInternalId(String currentId) {
try {
return Optional.of(getInternalId(currentId));
} catch (HttpClientErrorException e) {
if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
logNotFoundException(e, currentId);
} else {
logHttpException(e, currentId);
}
return Optional.empty();
} catch (Exception e) {
logException(e, currentId);
return Optional.empty();
}
}
String getInternalId(String currentId) {
return restTemplate.getForEntity(String.format(userManagerUrlTemplate, currentId), String.class).getBody();
}
void logNotFoundException(HttpClientErrorException e, String currentId) {
LOG.info(String.format("No matching user found by id %s- proceed without migration.", currentId), e);
}
void logHttpException(HttpClientErrorException e, String currentId) {
LOG.warn(String.format("Http Error %s while fetching internal id for external id %s. Proceeding with next user id",
e.getStatusCode().name(), currentId), e);
}
void logException(Exception e, String currentId) {
LOG.error(String.format("Error while fetching internal id for external id %s. Proceeding with next user id", currentId), e);
}
void logUrlIsNotConfigured() {
LOG.warn("UserManager url is not configured - user migration not started.");
}
@RollbackExecution
public void rollback() {
// kein rollback implementiert
}
}
{ {"properties": [
"properties": [
{ {
"name": "pluto.redirect.mail-from", "name": "pluto.redirect.mail-from",
"type": "java.lang.String", "type": "java.lang.String",
...@@ -19,6 +18,20 @@ ...@@ -19,6 +18,20 @@
"name": "pluto.forwarding.reply-to", "name": "pluto.forwarding.reply-to",
"type": "java.lang.String", "type": "java.lang.String",
"description": "Mail replyTo address for forwarding" "description": "Mail replyTo address for forwarding"
},
{
"name": "kop.production",
"type": "java.lang.String",
"description": "Enable production mode"
},
{
"name": "kop.osi.postfach.scheduler.enabled",
"type": "java.lang.String",
"description": "Enable or disable polling of osi postfach for new messages"
},
{
"name": "kop.usermanager.url",
"type": "java.lang.String",
"description": "Url of the user manager migration endpoint"
} }
] ]}
} \ No newline at end of file
\ No newline at end of file
...@@ -53,7 +53,8 @@ kop: ...@@ -53,7 +53,8 @@ kop:
index: test-index index: test-index
notification: notification:
mail-from: ea@ozg-sh.de mail-from: ea@ozg-sh.de
usermanager:
url: http://localhost:8080/api/migrate/user
aktenzeichen: de.itvsh.ozg.pluto.vorgang.AktenzeichenProviderEA aktenzeichen: de.itvsh.ozg.pluto.vorgang.AktenzeichenProviderEA
......
package de.itvsh.ozg.pluto.common.migration;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.UUID;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import de.itvsh.kop.common.test.DataITCase;
@DataITCase
class M012_MigrationUserIdITCase {
private final static String USER_MANAGER_URL = "userManagerUrlForTest";
@SpyBean
private M012_MigrationUserId migration;
@Autowired
private MigrationDbTestUtils dbUtils;
@Autowired
private MongoTemplate template;
@MockBean
private RestTemplate restTemplate;
@DisplayName("Migrate")
@Nested
class TestMigration {
@Mock
private Environment env;
@Test
void shouldInitMigration() {
when(env.getProperty(anyString())).thenReturn(USER_MANAGER_URL);
migration.doMigration(template, restTemplate, env);
verify(migration, never()).logUrlIsNotConfigured();
}
@Test
void shouldNotInitMigration() {
migration.doMigration(template, restTemplate, env);
verify(migration).logUrlIsNotConfigured();
}
@DisplayName("vorgang assignedTo")
@Nested
class TestMigrateAssignedTo {
private final static String ASSIGNED_TO = UUID.randomUUID().toString();
private final static String NEW_ASSIGNED_TO = new ObjectId().toHexString();
private Document vorgang;
@BeforeEach
void init() {
dbUtils.dropVorgangCollection();
vorgang = dbUtils.saveVorgang(createVorgangWithAssignedTo());
}
@SuppressWarnings("unchecked")
@Test
void shouldMigrateOnValidResponse() {
ResponseEntity<Object> response = mock(ResponseEntity.class);
when(response.getBody()).thenReturn(NEW_ASSIGNED_TO);
when(restTemplate.getForEntity(anyString(), any())).thenReturn(response);
migrateAssignedTo();
assertThat(loadVorgang()).containsEntry(M012_MigrationUserId.ASSIGNED_TO_FIELD, NEW_ASSIGNED_TO);
}
@DisplayName("on exception")
@Nested
class TestOnException {
@Test
void shouldProceed() {
mockHttpNotFound();
migrateAssignedTo();
assertThat(loadVorgang()).containsEntry(M012_MigrationUserId.ASSIGNED_TO_FIELD, ASSIGNED_TO);
}
@Test
void shouldLogOnHttpNotFound() {
mockHttpNotFound();
migrateAssignedTo();
verify(migration).logNotFoundException(any(), any());
}
@Test
void shouldLogOnHttpError() {
mockHttpError();
migrateAssignedTo();
verify(migration).logHttpException(any(), any());
}
}
private void migrateAssignedTo() {
migration.migrateAssignedTo();
}
private Document loadVorgang() {
return dbUtils.getVorgang(MigrationTestUtils.getObjectId(vorgang));
}
@DisplayName("find all vorgaenge with assignedTo field filled")
@Nested
class TestFindVorgangWithFilledAssignedTo {
@BeforeEach
void mock() {
dbUtils.dropVorgangCollection();
dbUtils.dropVorgangAttachedItemCollection();
}
@Test
void shouldFindWithOldAssignedToData() {
dbUtils.saveVorgang(createVorgangWithAssignedTo());
var vorgaenge = findVorgang();
assertThat(vorgaenge).hasSize(1);
assertThat(vorgaenge.get(0)).containsEntry(M012_MigrationUserId.ASSIGNED_TO_FIELD, ASSIGNED_TO);
}
@Test
void shouldReturnEmptyListOnNewAssignedToData() {
dbUtils.saveVorgang(new Document(M012_MigrationUserId.ASSIGNED_TO_FIELD, NEW_ASSIGNED_TO));
var vorgaenge = findVorgang();
assertThat(vorgaenge).isEmpty();
}
private List<Document> findVorgang() {
return template.find(migration.createFindVorgangWithFilledAssignedToQuery(), Document.class,
M012_MigrationUserId.COLLECTION_VORGANG_NAME);
}
}
private Document createVorgangWithAssignedTo() {
return new Document(M012_MigrationUserId.ASSIGNED_TO_FIELD, ASSIGNED_TO);
}
}
@DisplayName("command createdBy")
@Nested
class TestMigrateCreatedBy {
private final static String CREATED_BY = UUID.randomUUID().toString();
private final static String NEW_CREATED_BY = new ObjectId().toHexString();
private Document command;
@BeforeEach
void init() {
dbUtils.dropCommandCollection();
command = dbUtils.saveCommand(createCommandWithCreatedBy());
}
@SuppressWarnings("unchecked")
@Test
void shouldMigrateOnValidResponse() {
ResponseEntity<Object> response = mock(ResponseEntity.class);
when(response.getBody()).thenReturn(NEW_CREATED_BY);
when(restTemplate.getForEntity(anyString(), any())).thenReturn(response);
migrateCreatedBy();
assertThat(loadCommand()).containsEntry(M012_MigrationUserId.CREATED_BY_FIELD, NEW_CREATED_BY);
}
@DisplayName("on exception")
@Nested
class TestOnException {
@Test
void shouldProceed() {
mockHttpNotFound();
migrateCreatedBy();
assertThat(loadCommand()).containsEntry(M012_MigrationUserId.CREATED_BY_FIELD, CREATED_BY);
}
@Test
void shouldLogOnNotFound() {
mockHttpNotFound();
migrateCreatedBy();
verify(migration).logNotFoundException(any(), any());
}
@Test
void shouldLogOnHttpError() {
mockHttpError();
migrateCreatedBy();
verify(migration).logHttpException(any(), any());
}
}
private void migrateCreatedBy() {
migration.migrateCommandCreatedBy();
}
private Document loadCommand() {
return dbUtils.getCommand(MigrationTestUtils.getObjectId(command));
}
@DisplayName("find all commands with createdBy field filled")
@Nested
class TestFindCommandWithFilledCreatedBy {
@BeforeEach
void mock() {
dbUtils.dropCommandCollection();
}
@Test
void shouldFindWithOldCreatedByData() {
dbUtils.saveCommand(createCommandWithCreatedBy());
var commands = findCommand();
assertThat(commands).hasSize(1);
assertThat(commands.get(0)).containsEntry(M012_MigrationUserId.CREATED_BY_FIELD, CREATED_BY);
}
@Test
void shouldReturnEmptyListOnNewCreatedByData() {
dbUtils.saveVorgang(new Document(M012_MigrationUserId.CREATED_BY_FIELD, NEW_CREATED_BY));
var commands = findCommand();
assertThat(commands).isEmpty();
}
private List<Document> findCommand() {
return template.find(migration.createFindCommandWithCreatedByQuery(), Document.class,
M012_MigrationUserId.COLLECTION_COMMAND_NAME);
}
}
private static Document createCommandWithCreatedBy() {
return new Document(M012_MigrationUserId.CREATED_BY_FIELD, CREATED_BY);
}
}
@DisplayName("vorgangAttachedItem createdBy")
@Nested
class TestAttachedItemCreatedBy {
private final static String CREATED_BY = UUID.randomUUID().toString();
private final static String NEW_CREATED_BY = new ObjectId().toHexString();
private Document vorganAttachedItem;
@BeforeEach
void init() {
dbUtils.dropVorgangAttachedItemCollection();
vorganAttachedItem = dbUtils.saveVorgangAttachedItem(createVorgangAttachedItemWithCreatedBy());
}
@Test
void shouldMigrateOnValidResponse() {
doReturn(NEW_CREATED_BY).when(migration).getInternalId(anyString());
migrateVorgangAttachedItemCreatedBy();
assertThat(((Document) loadVorgangAttachedItem().get(M012_MigrationUserId.VORGANG_ATTACHED_ITEM_ITEM_FIELD)))
.containsEntry(M012_MigrationUserId.CREATED_BY_FIELD, NEW_CREATED_BY);
}
@DisplayName("on exception")
@Nested
class TestOnException {
@Test
void shouldProceedOnNotFound() {
mockHttpNotFound();
migrateVorgangAttachedItemCreatedBy();
assertThat((Document) loadVorgangAttachedItem().get(M012_MigrationUserId.VORGANG_ATTACHED_ITEM_ITEM_FIELD))
.containsEntry(M012_MigrationUserId.CREATED_BY_FIELD, CREATED_BY);
}
@Test
void shouldLogOnNotFound() {
mockHttpNotFound();
migrateVorgangAttachedItemCreatedBy();
verify(migration).logNotFoundException(any(), any());
}
@Test
void shouldLogOnHttpError() {
mockHttpError();
migrateVorgangAttachedItemCreatedBy();
verify(migration).logHttpException(any(), any());
}
}
private void migrateVorgangAttachedItemCreatedBy() {
migration.migrateVorgangAttachedItemCreatedBy();
}
private Document loadVorgangAttachedItem() {
return dbUtils.getVorgangAttachedItem(MigrationTestUtils.getObjectId(vorganAttachedItem));
}
@DisplayName("find all vorgangAttachedItem with createdBy field filled")
@Nested
class TestFindVorgangAttachedItemWithFilledCreatedBy {
@BeforeEach
void mock() {
dbUtils.dropVorgangAttachedItemCollection();
}
@Test
void shouldFindWithOldCreatedByData() {
dbUtils.saveVorgangAttachedItem(createVorgangAttachedItemWithCreatedBy());
var vorgangAttachedItems = findVorgangAttachedItem();
assertThat(vorgangAttachedItems).hasSize(1);
assertThat(((Document) vorgangAttachedItems.get(0).get(M012_MigrationUserId.VORGANG_ATTACHED_ITEM_ITEM_FIELD)))
.containsEntry(M012_MigrationUserId.CREATED_BY_FIELD, CREATED_BY);
}
@Test
void shouldReturnEmptyListOnNewCreatedByData() {
dbUtils.saveVorgangAttachedItem(new Document(M012_MigrationUserId.VORGANG_ATTACHED_ITEM_ITEM_FIELD,
new Document(M012_MigrationUserId.CREATED_BY_FIELD, NEW_CREATED_BY)));
var vorgangAttachedItems = findVorgangAttachedItem();
assertThat(vorgangAttachedItems).isEmpty();
}
private List<Document> findVorgangAttachedItem() {
return template.find(migration.createFindVorgangAttachedItemdWithCreatedByQuery(), Document.class,
M012_MigrationUserId.COLLECTION_VORGANG_ATTACHED_ITEM_NAME);
}
}
private static Document createVorgangAttachedItemWithCreatedBy() {
return new Document(M012_MigrationUserId.VORGANG_ATTACHED_ITEM_ITEM_FIELD,
new Document(M012_MigrationUserId.CREATED_BY_FIELD, CREATED_BY));
}
}
}
private void mockHttpNotFound() {
when(restTemplate.getForEntity(anyString(), any())).thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND));
}
private void mockHttpError() {
when(restTemplate.getForEntity(anyString(), any())).thenThrow(new HttpClientErrorException(HttpStatus.BAD_GATEWAY));
}
}
\ No newline at end of file
...@@ -40,10 +40,47 @@ class MigrationDbTestUtils { ...@@ -40,10 +40,47 @@ class MigrationDbTestUtils {
template.dropCollection(COMMAND_COLLECTION); template.dropCollection(COMMAND_COLLECTION);
} }
public void dropVorgangCollection() {
template.dropCollection(VORGANG_COLLECTION);
}
public Document getVorgang(ObjectId id) {
return template.findById(id, Document.class, VORGANG_COLLECTION);
}
public Document saveVorgang(Document doc) {
return template.save(doc, VORGANG_COLLECTION);
}
public Document saveVorgangAttachedItem(Document doc) {
return template.save(doc, VORGANG_ATTACHED_ITEM_COLLECTION);
}
public Document getVorgangAttachedItem(ObjectId id) {
return template.findById(id, Document.class, VORGANG_ATTACHED_ITEM_COLLECTION);
}
public void dropVorgangAttachedItemCollection() {
template.dropCollection(VORGANG_ATTACHED_ITEM_COLLECTION);
}
/**
* @param template
*
* @deprecated Inject MigrationDbTestUtils and use {@link #saveVorgang()}
* instead.
*/
@Deprecated
public static Document saveVorgang(MongoTemplate template, Document doc) { public static Document saveVorgang(MongoTemplate template, Document doc) {
return template.save(doc, VORGANG_COLLECTION); return template.save(doc, VORGANG_COLLECTION);
} }
/**
* @param template
*
* @deprecated Inject MigrationDbTestUtils and use
* {@link #saveVorgangAttachedItem()} instead.
*/
public static Document saveVorgangAttachedItem(MongoTemplate template, Document doc) { public static Document saveVorgangAttachedItem(MongoTemplate template, Document doc) {
return template.save(doc, VORGANG_ATTACHED_ITEM_COLLECTION); return template.save(doc, VORGANG_ATTACHED_ITEM_COLLECTION);
} }
...@@ -60,6 +97,13 @@ class MigrationDbTestUtils { ...@@ -60,6 +97,13 @@ class MigrationDbTestUtils {
return template.save(doc, BINARY_FILE_COLLECTION); return template.save(doc, BINARY_FILE_COLLECTION);
} }
/**
* @param template
*
* @deprecated Inject MigrationDbTestUtils and use {@link #getVorgang()}
* instead.
*/
@Deprecated
public static Document getVorgang(MongoTemplate template, ObjectId id) { public static Document getVorgang(MongoTemplate template, ObjectId id) {
return template.findById(id, Document.class, MigrationDbTestUtils.VORGANG_COLLECTION); return template.findById(id, Document.class, MigrationDbTestUtils.VORGANG_COLLECTION);
} }
...@@ -72,10 +116,24 @@ class MigrationDbTestUtils { ...@@ -72,10 +116,24 @@ class MigrationDbTestUtils {
template.dropCollection(BINARY_FILE_COLLECTION); template.dropCollection(BINARY_FILE_COLLECTION);
} }
/**
* @param template
*
* @deprecated Inject MigrationDbTestUtils and use
* {@link #dropVorgangCollection()} instead.
*/
@Deprecated
public static void dropVorgangCollection(MongoTemplate template) { public static void dropVorgangCollection(MongoTemplate template) {
template.dropCollection(VORGANG_COLLECTION); template.dropCollection(VORGANG_COLLECTION);
} }
/**
* @param template
*
* @deprecated Inject MigrationDbTestUtils and use
* {@link #dropVorgangAttachedItemCollection()} instead.
*/
@Deprecated
public static void dropVorgangAttachedItemCollection(MongoTemplate template) { public static void dropVorgangAttachedItemCollection(MongoTemplate template) {
template.dropCollection(VORGANG_ATTACHED_ITEM_COLLECTION); template.dropCollection(VORGANG_ATTACHED_ITEM_COLLECTION);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment