/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community 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://opensource.org/licenses/ecl2.txt
*
* 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.opencastproject.assetmanager.migration;
import static com.entwinemedia.fn.Stream.$;
import static com.entwinemedia.fn.fns.Strings.isBlank;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.opencastproject.assetmanager.api.fn.Enrichments.enrich;
import org.opencastproject.assetmanager.api.Snapshot;
import org.opencastproject.assetmanager.api.query.AQueryBuilder;
import org.opencastproject.assetmanager.api.query.RichAResult;
import org.opencastproject.assetmanager.impl.AbstractAssetManagerTestBase;
import org.opencastproject.util.IoSupport;
import org.opencastproject.util.persistencefn.Queries;
import com.entwinemedia.fn.Fn;
import com.entwinemedia.fn.Fn2;
import com.entwinemedia.fn.P2;
import com.entwinemedia.fn.Pred;
import com.entwinemedia.fn.Products;
import com.entwinemedia.fn.Stream;
import com.entwinemedia.fn.Unit;
import com.entwinemedia.fn.fns.Strings;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import javax.persistence.EntityManager;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
/**
* Test the migration of {@link org.opencastproject.archive.api.Archive} tables
* to {@link org.opencastproject.assetmanager.api.AssetManager} tables on a MySQL database.
* <p>
* The test is set to "ignore" since it requires some system properties to be set.
* <ul>
* <li>-Dtest-database-url, JDBC URL</li>
* <li>-Dtest-database-user, defaults to 'matterhorn'</li>
* <li>-Dtest-database-password, defaults to 'matterhorn'</li>
* </ul>
*/
@RunWith(JUnitParamsRunner.class)
@Ignore
public class ArchiveToAssetManagerMigrationTest extends AbstractAssetManagerTestBase {
private static final Logger logger = LoggerFactory.getLogger(ArchiveToAssetManagerMigrationTest.class);
@Test
@Parameters
public void testMigration(String testData, Stream<String> seriesIds) throws Exception {
// setup database and apply the DDL migration
penv.tx(runStatements("/mysql-reset.sql"));
penv.tx(runStatements("/mysql-archive-schema.sql"));
// insert test data since it makes a difference to migrate a database with or without data
penv.tx(runStatements(testData));
// schema migration pre-processing
penv.tx(runStatements("/mysql-migration-1.sql"));
// data migration
penv.tx(runStatements("/mysql-migration-2.sql"));
// schema migration post-processing and cleanup
penv.tx(runStatements("/mysql-migration-3.sql"));
//
// compare actual and expected DDL
final String showCreateTables =
$("mh_assets_snapshot", "mh_assets_asset", "mh_assets_properties", "mh_assets_version_claim", "SEQUENCE")
.map(Strings.wrap("SHOW CREATE TABLE ", ""))
.bind(findAll)
.map(take(1))
.mkString(";\n\n")
.concat(";");
logger.info("+ " + showCreateTables);
assertEquals("Migrated database should be equal to DDL in Ansible playbook",
IoSupport.loadFileFromClassPathAsString("/mysql-assetmanager-schema.sql").get().trim(),
showCreateTables);
// check sequence table migration
assertTrue("Sequence table should be updated",
penv.tx(Queries.sql.findSingle("SELECT * FROM SEQUENCE WHERE SEQ_NAME='seq_mh_assets_asset'")).isSome());
assertTrue("Sequence table should be updated",
penv.tx(Queries.sql.findSingle("SELECT * FROM SEQUENCE WHERE SEQ_NAME='seq_mh_assets_snapshot'")).isSome());
//
// if the migration completed successfully the asset manager should be able to run some operations
final AQueryBuilder q = am.createQuery();
{
// run series checks
for (String seriesId : seriesIds) {
final RichAResult r = enrich(q.select(q.snapshot()).where(q.seriesId().eq(seriesId)).run());
assertTrue(r.getSize() > 0);
for (Snapshot e : r.getSnapshots()) {
assertEquals(seriesId, e.getMediaPackage().getSeries());
}
}
}
// add some media packages
final String[] mp = createAndAddMediaPackagesSimple(5, 1, 1);
for (String id : mp) {
am.setProperty(p.agent.mk(id, "agent"));
}
{
final RichAResult r = enrich(q.select(q.snapshot(), q.properties()).run());
assertEquals(59, r.getSize());
assertEquals(59, r.countSnapshots());
assertEquals(5, r.countProperties());
}
}
private Object parametersForTestMigration() {
return $a(
// test data set ETH
$a("/mysql-archive-test-data-eth.sql",
// series IDs
$("6255c2c3-3cbc-4178-bc56-a836828a00e9",
"9054382b-84d2-41a3-96d9-83fafb0187ea",
"bcf76afe-81bb-453e-ba44-84aa7da3bb93",
"d72f201a-7240-49f4-be84-2be8e6aa4ab4",
"df2ecc72-74d8-489c-b026-93f1068bcbcc",
"d47137c3-4443-4e19-ac91-be0d3e6fa5cf",
"229d85e9-9b63-4e0c-b2c7-ea100bf71d09",
"1627a55a-dcff-453c-bf5c-49ed08779508",
"c2e23a6c-df2c-4a62-8e88-ea57973f4ce8",
"d47137c3-4443-4e19-ac91-be0d3e6fa5cf",
"63257b6b-170f-415a-967e-63483e7b099b",
"3ce79c93-16e2-4f85-a801-6ba50496ad39",
"28dc4731-16f3-4f9c-83f7-46977cf107a6",
"4e4e8a62-accf-4d23-83b0-4205c937206a",
"61aecb82-c768-4425-9bf6-14bbd8f30d2d",
"a6081e43-579a-47a8-81ac-90e05e2751c8",
"d690e96c-5a51-466c-aa76-f719983441ed",
"c7b29d08-5961-47aa-bf4f-e5ea6f169e0f",
"44a5a6a5-3ab3-41e9-b44d-a32140ff2895",
"099c0a5d-9db3-48f4-80da-c04f72bdbd7f",
"63257b6b-170f-415a-967e-63483e7b099b",
"52ac1672-b92e-4b78-9d8d-9a7a3b6af9c2",
"bcf76afe-81bb-453e-ba44-84aa7da3bb93",
"3ea70247-d36a-4062-9275-7bd0c1fae6c0",
"379a5d41-9433-49aa-af68-e52e28a37439",
"3af61206-199f-43e8-8429-3f4beb454b8b",
"61cb1e7b-5119-48b7-af90-1c98ed3631b9",
"c7b29d08-5961-47aa-bf4f-e5ea6f169e0f",
"d1a94a70-cea5-4361-b4dd-9991f42f0995",
"edeb17d5-fc71-4382-8b38-3aa4306e8b70",
"77e37ca2-faac-4220-b47c-e62233258b97",
"12a91df9-43e6-4bb6-8012-841add7e2c3d",
"cdd28f8e-4dee-407b-8935-8f0502b6638c",
"7c6d5e1a-6f38-4792-b85e-a845af5e7fad",
"63257b6b-170f-415a-967e-63483e7b099b",
"43f52c96-73ed-41ad-b171-901df1b19575",
"f254ad0a-a2e5-476d-bfb0-8da90224ecff",
"0aae8ad7-c477-460b-83c1-27f6617d2f84",
"ab72f2cc-bb4a-4f74-b063-2ece174416e7",
"2e2d5120-f2f5-46c9-9c75-96c90b610bcb",
"d2ff202b-ae12-4ddf-a43e-a33a8ded49fe",
"5e421af6-1fa7-487b-a3b8-b472661a926c",
"c5965f27-f700-4a0e-8608-f79b260fa9f3",
"099416b6-bcad-4e7d-bf8e-38a90a23e53a",
"08075abd-df47-4719-840b-2a32c15e5530")),
// test data set 2
$a("/mysql-archive-test-data-2.sql",
// series IDs
$("f4a7f0e4-fc4f-46b5-bdbc-4eade7b49146",
"da36a1d593b325b4ff91509fa058264f",
"8b5666fba8228a26ebdbae6c2bcaae1a",
"3a0c24228bd614d94252ac42fa879be3",
"2C444DC641EAE69AE102F147B6E9D505",
"V_841a8dee9896b4be50680fe21c5d3a5f",
"2C444DC641EAE69AE102F147B6E9D956",
"C24D3664B85FAE65E7A6ABC2EEE717D1",
"2C444DC641EAE69AE102F147B6E9D79E",
"V_fc0d286d87c76d62cabe7bff590699eb",
"bebe2c15a1a931911a65cb0003576109",
"a309e0443ed822d3d9506cc2c6f269ec",
"7aa741c5978dc777b916366cb5efd8b7",
"7d810947260ced5af4c370e21011365f",
"349F8D69FDA3DEB3E86B16CFF9B7045B",
"9bbb092844a6c1adac41e4da8fcebeaf",
"V_eb16d184e6a92bac2b99d049d69762ca",
"3bf1ef11-0fa6-4def-9dfb-45f1c7b763a8",
"V_b9e41816cfb21c305ae1f168dd6ace8b")));
}
/* ------------------------------------------------------------------------------------------------------------------ */
@Test
public void testMh01StageMigration() {
penv.tx(runStatements("/test_mh01_stage_migration.sql"));
// schema migration pre-processing
penv.tx(runStatements("/mysql-migration-1.sql"));
// data migration
penv.tx(runStatements("/mysql-migration-2.sql"));
// schema migration post-processing and cleanup
penv.tx(runStatements("/mysql-migration-3.sql"));
}
private Fn<EntityManager, Unit> runStatements(String classPathResource) {
return runStatements(readStatements(classPathResource));
}
private Fn<EntityManager, Unit> runStatements(final Stream<String> statements) {
return new Fn<EntityManager, Unit>() {
@Override public Unit apply(EntityManager em) {
for (String statement : statements) {
logger.info("+++ " + statement);
Queries.sql.update(em, statement);
}
return Unit.unit;
}
};
}
private Fn<String, List<Object[]>> findAll = new Fn<String, List<Object[]>>() {
@Override public List<Object[]> apply(String statement) {
return penv.tx(Queries.sql.<Object[]>findAll(statement));
}
};
private <A> Fn<A[], A> take(final int index) {
return new Fn<A[], A>() {
@Override public A apply(A[] as) {
return as[index];
}
};
}
private Stream<String> readStatements(String classPathResource) {
for (String file : IoSupport.loadFileFromClassPathAsString(classPathResource)) {
return $(file.split("\\n"))
.map(Strings.trim)
.filter((startsWith("--").or(startsWith("#")).or(isBlank)).not())
.foldl(Products.E.p2("", Stream.<String>empty()), new Fn2<P2<String, Stream<String>>, String, P2<String, Stream<String>>>() {
@Override public P2<String, Stream<String>> apply(P2<String, Stream<String>> statements, String line) {
if (line.endsWith(";")) {
return Products.E.p2("", statements.get2().append($(statements.get1() + "\n" + line)));
} else {
return Products.E.p2(statements.get1() + "\n" + line, statements.get2());
}
}
}).get2();
}
throw new RuntimeException("Cannot read SQL file " + classPathResource);
}
private Pred<String> startsWith(final String a) {
return new Pred<String>() {
@Override public Boolean apply(String s) {
return s.startsWith(a);
}
};
}
}