-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #551 from folio-org/modfqmmgr-594
* other files update * add comment * logging * test fix * debug * safer updates * properly iterate dates * more fix * Test migration utils * add exceptional check * refactor migration constant storage to remove cycle * Add V4 specific migration test * fix test * imports * Use proper suffixes for dates
- Loading branch information
Showing
13 changed files
with
645 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
src/main/java/org/folio/fqm/config/MigrationConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.folio.fqm.config; | ||
|
||
import java.util.UUID; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class MigrationConfiguration { | ||
|
||
public static final String VERSION_KEY = "_version"; | ||
public static final UUID REMOVED_ENTITY_TYPE_ID = UUID.fromString("deadbeef-dead-dead-dead-deaddeadbeef"); | ||
|
||
private static final String CURRENT_VERSION = "5"; | ||
// TODO: replace this with current version in the future? | ||
private static final String DEFAULT_VERSION = "0"; | ||
|
||
public String getCurrentVersion() { | ||
return CURRENT_VERSION; | ||
} | ||
|
||
public String getDefaultVersion() { | ||
return DEFAULT_VERSION; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 15 additions & 8 deletions
23
src/main/java/org/folio/fqm/migration/MigrationStrategyRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,31 @@ | ||
package org.folio.fqm.migration; | ||
|
||
import java.util.List; | ||
import org.folio.fqm.client.ConfigurationClient; | ||
import org.folio.fqm.migration.strategies.V0POCMigration; | ||
import org.folio.fqm.migration.strategies.V1ModeOfIssuanceConsolidation; | ||
import org.folio.fqm.migration.strategies.V2ResourceTypeConsolidation; | ||
import org.folio.fqm.migration.strategies.V3RamsonsFieldCleanup; | ||
import org.folio.fqm.migration.strategies.V4DateFieldTimezoneAddition; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class MigrationStrategyRepository { | ||
|
||
// prevent re-initialization on each call | ||
private static final List<MigrationStrategy> MIGRATION_STRATEGIES = List.of( | ||
new V0POCMigration(), | ||
new V1ModeOfIssuanceConsolidation(), | ||
new V2ResourceTypeConsolidation(), | ||
new V3RamsonsFieldCleanup() | ||
); | ||
private final List<MigrationStrategy> migrationStrategies; | ||
|
||
public MigrationStrategyRepository(ConfigurationClient configurationClient) { | ||
this.migrationStrategies = | ||
List.of( | ||
new V0POCMigration(), | ||
new V1ModeOfIssuanceConsolidation(), | ||
new V2ResourceTypeConsolidation(), | ||
new V3RamsonsFieldCleanup(), | ||
new V4DateFieldTimezoneAddition(configurationClient) | ||
); | ||
} | ||
|
||
public List<MigrationStrategy> getMigrationStrategies() { | ||
return MIGRATION_STRATEGIES; | ||
return this.migrationStrategies; | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
src/main/java/org/folio/fqm/migration/MigrationUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package org.folio.fqm.migration; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonMappingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.node.ArrayNode; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import java.io.UncheckedIOException; | ||
import java.util.Optional; | ||
import java.util.function.UnaryOperator; | ||
import lombok.experimental.UtilityClass; | ||
import lombok.extern.log4j.Log4j2; | ||
import org.apache.commons.lang3.function.TriConsumer; | ||
import org.folio.fqm.config.MigrationConfiguration; | ||
|
||
@Log4j2 | ||
@UtilityClass | ||
public class MigrationUtils { | ||
|
||
private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
|
||
/** | ||
* Helper function to transform an FQL query. This changes a version to a new one, and runs a given | ||
* function on each field in the query. See {@link #migrateFqlTree(ObjectNode, TriConsumer)} for more | ||
* details on the field transformation function. | ||
* | ||
* @param fqlQuery The root query to migrate | ||
* @param versionTransformer A function that takes the current (potentially null) version and | ||
* returns the new one to be persisted in the query | ||
* @param handler something that takes the result node, the field name, and the field's query object, | ||
* applies some transformation, and stores the results back in result | ||
* @throws JsonMappingException | ||
* @throws JsonProcessingException | ||
*/ | ||
public static String migrateFql( | ||
String fqlQuery, | ||
UnaryOperator<String> versionTransformer, | ||
TriConsumer<ObjectNode, String, JsonNode> handler | ||
) { | ||
try { | ||
ObjectNode fql = (ObjectNode) objectMapper.readTree(fqlQuery); | ||
|
||
fql.set( | ||
MigrationConfiguration.VERSION_KEY, | ||
objectMapper.valueToTree( | ||
versionTransformer.apply( | ||
Optional.ofNullable(fql.get(MigrationConfiguration.VERSION_KEY)).map(JsonNode::asText).orElse(null) | ||
) | ||
) | ||
); | ||
|
||
fql = migrateFqlTree(fql, handler); | ||
|
||
return objectMapper.writeValueAsString(fql); | ||
} catch (JsonProcessingException e) { | ||
log.error("Unable to process JSON", e); | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
|
||
/** | ||
* Call `handler` for each field in the FQL query tree, returning a new tree. | ||
* Note that `handler` is responsible for inserting what should be left in the tree, if anything; | ||
* if the function is a no-op, an empty FQL tree will be returned. | ||
* | ||
* A true "no-op" here would look like (result, key, value) -> result.set(key, value). | ||
* | ||
* This conveniently handles `$and`s, allowing logic to be handled on fields only. | ||
* | ||
* @param fql the fql node | ||
* @param handler something that takes the result node, the field name, and the field's query object, | ||
* applies some transformation, and stores the results back in result | ||
* @return | ||
*/ | ||
private static ObjectNode migrateFqlTree(ObjectNode fql, TriConsumer<ObjectNode, String, JsonNode> handler) { | ||
ObjectNode result = new ObjectMapper().createObjectNode(); | ||
// iterate through fields in source | ||
fql | ||
.fields() | ||
.forEachRemaining(entry -> { | ||
if ("$and".equals(entry.getKey())) { | ||
ArrayNode resultContents = new ObjectMapper().createArrayNode(); | ||
((ArrayNode) entry.getValue()).elements() | ||
.forEachRemaining(node -> { | ||
ObjectNode innerResult = migrateFqlTree((ObjectNode) node, handler); | ||
// handle removed fields | ||
if (!innerResult.isEmpty()) { | ||
resultContents.add(innerResult); | ||
} | ||
}); | ||
result.set("$and", resultContents); | ||
// ensure we don't run this on the _version | ||
} else if (!MigrationConfiguration.VERSION_KEY.equals(entry.getKey())) { | ||
handler.accept(result, entry.getKey(), entry.getValue()); | ||
} else { | ||
// keep _version as-is | ||
result.set(entry.getKey(), entry.getValue()); | ||
} | ||
}); | ||
|
||
return result; | ||
} | ||
} |
Oops, something went wrong.