/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.platform.dataaccess.datasource.wizard.service.impl;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import junit.framework.Assert;
import org.apache.commons.io.IOUtils;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.pentaho.agilebi.modeler.ModelerWorkspace;
import org.pentaho.agilebi.modeler.gwt.GwtModelerWorkspaceHelper;
import org.pentaho.agilebi.modeler.util.TableModelerSource;
import org.pentaho.database.model.IDatabaseConnection;
import org.pentaho.di.core.KettleEnvironment;
import org.pentaho.di.core.Props;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.metadata.model.Domain;
import org.pentaho.metadata.model.olap.OlapDimension;
import org.pentaho.metadata.repository.IMetadataDomainRepository;
import org.pentaho.platform.api.data.IDBDatasourceService;
import org.pentaho.platform.api.engine.IAuthorizationPolicy;
import org.pentaho.platform.api.engine.IPentahoDefinableObjectFactory;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.IPluginResourceLoader;
import org.pentaho.platform.api.engine.ISecurityHelper;
import org.pentaho.platform.api.engine.ISolutionEngine;
import org.pentaho.platform.api.engine.IUserRoleListService;
import org.pentaho.platform.api.mimetype.IMimeType;
import org.pentaho.platform.api.repository.IClientRepositoryPathsStrategy;
import org.pentaho.platform.api.repository.datasource.DatasourceMgmtServiceException;
import org.pentaho.platform.api.repository.datasource.DuplicateDatasourceException;
import org.pentaho.platform.api.repository.datasource.IDatasourceMgmtService;
import org.pentaho.platform.api.repository.datasource.NonExistingDatasourceException;
import org.pentaho.platform.api.repository2.unified.IPlatformImportBundle;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.core.mimetype.MimeType;
import org.pentaho.platform.dataaccess.datasource.api.AnalysisService;
import org.pentaho.platform.dataaccess.datasource.api.resources.DataSourceWizardResource;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.core.system.SystemSettings;
import org.pentaho.platform.engine.core.system.boot.PlatformInitializationException;
import org.pentaho.platform.engine.services.connection.datasource.dbcp.JndiDatasourceService;
import org.pentaho.platform.engine.services.solution.SolutionEngine;
import org.pentaho.platform.plugin.action.mondrian.catalog.IMondrianCatalogService;
import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalogHelper;
import org.pentaho.platform.plugin.action.mondrian.mapper.MondrianOneToOneUserRoleListMapper;
import org.pentaho.platform.plugin.services.connections.mondrian.MDXConnection;
import org.pentaho.platform.plugin.services.connections.mondrian.MDXOlap4jConnection;
import org.pentaho.platform.plugin.services.connections.sql.SQLConnection;
import org.pentaho.platform.plugin.services.importer.IPlatformImporter;
import org.pentaho.platform.plugin.services.importer.MetadataImportHandler;
import org.pentaho.platform.plugin.services.importer.MondrianImportHandler;
import org.pentaho.platform.plugin.services.importer.PlatformImportException;
import org.pentaho.platform.plugin.services.importer.RepositoryFileImportBundle;
import org.pentaho.platform.plugin.services.metadata.IPentahoMetadataDomainRepositoryImporter;
import org.pentaho.platform.plugin.services.metadata.PentahoMetadataDomainRepository;
import org.pentaho.platform.plugin.services.pluginmgr.PluginClassLoader;
import org.pentaho.platform.plugin.services.pluginmgr.PluginResourceLoader;
import org.pentaho.platform.repository2.unified.RepositoryUtils;
import org.pentaho.platform.repository2.unified.fs.FileSystemBackedUnifiedRepository;
import org.pentaho.test.platform.engine.core.MicroPlatform;
import org.pentaho.test.platform.engine.security.MockSecurityHelper;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.sun.jersey.core.header.FormDataContentDisposition;
public class DatasourceResourceIT {
private static MicroPlatform mp;
@BeforeClass
public static void setUp() throws Exception {
System.setProperty( "org.osjava.sj.root", "target/test-classes/solution1/system/simple-jndi" ); //$NON-NLS-1$ //$NON-NLS-2$
mp = new MicroPlatform( "target/test-classes/solution1" );
IAuthorizationPolicy mockAuthorizationPolicy = mock( IAuthorizationPolicy.class );
when( mockAuthorizationPolicy.isAllowed( anyString() ) ).thenReturn( true );
IUserRoleListService mockUserRoleListService = mock( IUserRoleListService.class );
IDataAccessPermissionHandler mockDataAccessPermHandler = mock( IDataAccessPermissionHandler.class );
when( mockDataAccessPermHandler.hasDataAccessPermission( any( IPentahoSession.class ) ) ).thenReturn( true );
mp.define( ISolutionEngine.class, SolutionEngine.class, IPentahoDefinableObjectFactory.Scope.GLOBAL );
mp.define( IUnifiedRepository.class, TestFileSystemBackedUnifiedRepository.class, IPentahoDefinableObjectFactory.Scope.GLOBAL );
mp.define( IMondrianCatalogService.class, MondrianCatalogHelper.class, IPentahoDefinableObjectFactory.Scope.GLOBAL );
mp.define( "connection-SQL", SQLConnection.class );
mp.define( "connection-MDX", MDXConnection.class );
mp.define( "connection-MDXOlap4j", MDXOlap4jConnection.class );
mp.define( IDBDatasourceService.class, JndiDatasourceService.class, IPentahoDefinableObjectFactory.Scope.GLOBAL );
mp.define( MDXConnection.MDX_CONNECTION_MAPPER_KEY, MondrianOneToOneUserRoleListMapper.class, IPentahoDefinableObjectFactory.Scope.GLOBAL );
mp.define( IDatasourceMgmtService.class, MockDatasourceMgmtService.class );
mp.define( IClientRepositoryPathsStrategy.class, MockClientRepositoryPathsStrategy.class );
mp.define( ISecurityHelper.class, MockSecurityHelper.class );
mp.define( UserDetailsService.class, MockUserDetailService.class );
mp.define( "singleTenantAdminUserName", "admin" );
mp.defineInstance( IMetadataDomainRepository.class, createMetadataDomainRepository() );
mp.defineInstance( IAuthorizationPolicy.class, mockAuthorizationPolicy );
mp.defineInstance( IPluginResourceLoader.class, new PluginResourceLoader() {
protected PluginClassLoader getOverrideClassloader() {
return new PluginClassLoader( new File( ".", "target/test-classes/solution1/system/simple-jndi" ), this );
}
} );
mp.defineInstance( IUserRoleListService.class, mockUserRoleListService );
mp.defineInstance( IDataAccessPermissionHandler.class, mockDataAccessPermHandler );
mp.setSettingsProvider( new SystemSettings() );
mp.start();
PentahoSessionHolder.setStrategyName( PentahoSessionHolder.MODE_GLOBAL );
SecurityContextHolder.setStrategyName( SecurityContextHolder.MODE_GLOBAL );
}
@Before
@After
public void clearDSWData() {
File repoData = new File( "target/test-classes/dsw/etc" );
if ( repoData.exists() && repoData.isDirectory() ) {
clearDir( repoData );
}
}
private void clearDir( File dir ) {
if ( dir.isDirectory() ) {
for ( File file : dir.listFiles() ) {
if ( file.isDirectory() ) {
clearDir( file );
} else {
file.delete();
}
}
}
}
@Test
public void DummyTest() throws Exception {
}
@Test
public void testMondrianImportExport() throws Exception {
final String domainName = "SalesData";
List<IMimeType> mimeTypeList = new ArrayList<IMimeType>();
mimeTypeList.add( new MimeType( "Mondrian", "mondrian.xml" ) );
System.setProperty( "org.osjava.sj.root", "target/test-classes/solution1/system/simple-jndi" ); //$NON-NLS-1$ //$NON-NLS-2$
File mondrian = new File( "target/test-classes/dsw/testData/SalesData.mondrian.xml" );
RepositoryFile repoMondrianFile = new RepositoryFile.Builder( mondrian.getName() ).folder( false ).hidden( false )
.build();
RepositoryFileImportBundle bundle1 = new RepositoryFileImportBundle.Builder()
.file( repoMondrianFile ).charSet( "UTF-8" ).input( new FileInputStream( mondrian ) ).mime( "mondrian.xml" )
.withParam( "parameters", "Datasource=Pentaho;overwrite=true" ).withParam( "domain-id", "SalesData" ).build();
MondrianImportHandler mondrianImportHandler = new MondrianImportHandler( mimeTypeList,
PentahoSystem.get( IMondrianCatalogService.class ) );
mondrianImportHandler.importFile( bundle1 );
try {
KettleEnvironment.init();
Props.init( Props.TYPE_PROPERTIES_EMPTY );
} catch ( Exception e ) {
// may already be initialized by another test
}
Domain domain = generateModel();
ModelerWorkspace model = new ModelerWorkspace( new GwtModelerWorkspaceHelper() );
model.setModelName( "ORDERS" );
model.setDomain( domain );
model.getWorkspaceHelper().populateDomain( model );
new ModelerService().serializeModels( domain, domainName );
final Response salesData = new DataSourceWizardResource().doGetDSWFilesAsDownload( domainName + ".xmi" );
Assert.assertEquals( salesData.getStatus(), Response.Status.OK.getStatusCode() );
Assert.assertNotNull( salesData.getMetadata() );
Assert.assertNotNull( salesData.getMetadata().getFirst( "Content-Disposition" ) );
Assert.assertEquals( salesData.getMetadata().getFirst( "Content-Disposition" ).getClass(), String.class );
Assert.assertTrue( ( (String) salesData.getMetadata().getFirst( "Content-Disposition" ) ).endsWith( domainName + ".zip\"" ) );
File file = File.createTempFile( domainName, ".zip" );
final FileOutputStream fileOutputStream = new FileOutputStream( file );
( (StreamingOutput) salesData.getEntity() ).write( fileOutputStream );
fileOutputStream.close();
final ZipFile zipFile = new ZipFile( file );
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while ( entries.hasMoreElements() ) {
final ZipEntry zipEntry = entries.nextElement();
Assert.assertTrue( zipEntry.getName().equals( domainName + ".xmi" ) || zipEntry.getName().equals( domainName + ".mondrian.xml" ) );
}
zipFile.close();
file.delete();
}
@Test
public void testMetadataImportExport() throws PlatformInitializationException, IOException, PlatformImportException {
List<IMimeType> mimeTypeList = new ArrayList<IMimeType>();
mimeTypeList.add( new MimeType( "Metadata", ".xmi" ) );
File metadata = new File( "target/test-classes/dsw/testData/metadata.xmi" );
RepositoryFile repoMetadataFile = new RepositoryFile.Builder( metadata.getName() ).folder( false ).hidden( false )
.build();
MetadataImportHandler metadataImportHandler = new MetadataImportHandler( mimeTypeList,
(IPentahoMetadataDomainRepositoryImporter) PentahoSystem.get( IMetadataDomainRepository.class ) );
RepositoryFileImportBundle bundle1 = new RepositoryFileImportBundle.Builder()
.file( repoMetadataFile ).charSet( "UTF-8" ).input( new FileInputStream( metadata ) ).mime( ".xmi" ).withParam(
"domain-id", "SalesData" ).build();
metadataImportHandler.importFile( bundle1 );
final Response salesData = new DataSourceWizardResource().doGetDSWFilesAsDownload( "SalesData" );
Assert.assertEquals( salesData.getStatus(), Response.Status.OK.getStatusCode() );
Assert.assertNotNull( salesData.getMetadata() );
Assert.assertNotNull( salesData.getMetadata().getFirst( "Content-Disposition" ) );
Assert.assertEquals( salesData.getMetadata().getFirst( "Content-Disposition" ).getClass(), String.class );
Assert.assertTrue( ( (String) salesData.getMetadata().getFirst( "Content-Disposition" ) ).endsWith( ".xmi\"" ) );
}
@Test
public void testPublishDsw() throws Exception {
DataSourceWizardResource service = new DataSourceWizardResource();
Mockery mockery = new Mockery();
final IPlatformImporter mockImporter = mockery.mock( IPlatformImporter.class );
mp.defineInstance( IPlatformImporter.class, mockImporter );
mockery.checking( new Expectations() {
{
oneOf( mockImporter ).importFile( with( match( new TypeSafeMatcher<IPlatformImportBundle>() {
public boolean matchesSafely( IPlatformImportBundle bundle ) {
return bundle.isPreserveDsw() && bundle.getProperty( "domain-id" ).equals( "AModel.xmi" )
&& bundle.getMimeType().equals( "text/xmi+xml" );
}
public void describeTo( Description description ) {
description.appendText( "bundle with xmi" );
}
} ) ) );
oneOf( mockImporter ).importFile( with( match( new TypeSafeMatcher<IPlatformImportBundle>() {
public boolean matchesSafely( IPlatformImportBundle bundle ) {
return bundle.getProperty( "domain-id" ).equals( "AModel" )
&& bundle.getMimeType().equals( "application/vnd.pentaho.mondrian+xml" );
}
public void describeTo( Description description ) {
description.appendText( "bundle with mondrian schema" );
}
} ) ) );
}
} );
FileInputStream in = new FileInputStream( new File( new File( "target/test-classes" ), "SampleDataOlap.xmi" ) );
try {
Response resp = service.publishDsw( "AModel.xmi", in, true, false, null );
Assert.assertEquals(
Response.Status.Family.SUCCESSFUL,
Response.Status.fromStatusCode( resp.getStatus() ).getFamily() );
mockery.assertIsSatisfied();
} finally {
IOUtils.closeQuietly( in );
}
}
private void testImportFile( String filePath, final String expectedSchemaName ) throws Exception {
FormDataContentDisposition schemaFileInfo = mock( FormDataContentDisposition.class );
when( schemaFileInfo.getFileName() ).thenReturn( "stubFileName" );
Mockery mockery = new Mockery();
final IPlatformImporter mockImporter = mockery.mock( IPlatformImporter.class );
mp.defineInstance( IPlatformImporter.class, mockImporter );
mockery.checking( new Expectations() {
{
oneOf( mockImporter ).importFile( with( match( new TypeSafeMatcher<IPlatformImportBundle>() {
public boolean matchesSafely( IPlatformImportBundle bundle ) {
return bundle.getProperty( "domain-id" ).equals( expectedSchemaName )
&& bundle.getMimeType().equals( "application/vnd.pentaho.mondrian+xml" );
}
public void describeTo( Description description ) {
description.appendText( "bundle with mondrian schema" );
}
} ) ) );
}
} );
AnalysisService service = new AnalysisService();
FileInputStream in = new FileInputStream( filePath );
try {
service.putMondrianSchema( in, schemaFileInfo, null, null, null, false, false,
"Datasource=SampleData;overwrite=false", null );
mockery.assertIsSatisfied();
} finally {
IOUtils.closeQuietly( in );
}
}
private void testImportZipFile( String filePath, final String expectedSchemaName, boolean annotated ) throws Exception {
FormDataContentDisposition schemaFileInfo = mock( FormDataContentDisposition.class );
File f = new File( filePath );
when( schemaFileInfo.getFileName() ).thenReturn( f.getName() );
Mockery mockery = new Mockery();
final IPlatformImporter mockImporter = mockery.mock( IPlatformImporter.class );
mp.defineInstance( IPlatformImporter.class, mockImporter );
if ( annotated ) {
mockery.checking( new Expectations() {
{
exactly( 2 ).of( mockImporter ).importFile( with( match( new TypeSafeMatcher<IPlatformImportBundle>() {
public boolean matchesSafely( IPlatformImportBundle bundle ) {
return true;
}
public void describeTo( Description description ) {
description.appendText( "bundle with zipped mondrian schema" );
}
} ) ) );
}
} );
} else {
mockery.checking( new Expectations() {
{
oneOf( mockImporter ).importFile( with( match( new TypeSafeMatcher<IPlatformImportBundle>() {
public boolean matchesSafely( IPlatformImportBundle bundle ) {
return bundle.getProperty( "domain-id" ).equals( expectedSchemaName );
}
public void describeTo( Description description ) {
description.appendText( "bundle with zipped mondrian schema" );
}
} ) ) );
}
} );
}
AnalysisService service = new AnalysisService();
FileInputStream in = new FileInputStream( filePath );
try {
service.putMondrianSchema( in, schemaFileInfo, null, null, null, false, false,
"Datasource=SampleData;overwrite=false", null );
mockery.assertIsSatisfied();
} finally {
IOUtils.closeQuietly( in );
}
}
@Test
public void testImportMondrianSchemaWithEncodingIssue() throws Exception {
testImportFile( "target/test-classes/mysql_steelwheels.mondri_xyz-invalid_encoding.xml", "SteelWheels_xyzxx" );
}
@Test
public void testImportMondrianSchemaWithXmlFormatIssue() throws Exception {
testImportFile( "target/test-classes/mysql_steelwheels.mondri_xyz-invalid_xml.xml", "SteelWheels_xyzxx1" );
}
@Test
public void testImportMondrianSchemaWithDeclaringEncodingIssue() throws Exception {
testImportFile( "target/test-classes/mysql_steelwheels.mondri_xyz-invalid_declare_encoding.xml", "SteelWheels_xyzxx" );
}
@Test
public void testImportMondrianSchemaFromZip() throws Exception {
testImportZipFile( "target/test-classes/mysql_steelwheels_no_annot.zip", "SteelWheels_no_annot", false );
}
@Test
public void testImportAnnotatedMondrianSchemaFromZip() throws Exception {
testImportZipFile( "target/test-classes/mysql_steelwheels_qq.zip", "SteelWheels_qq", true );
}
@Factory
public static <T> Matcher<T> match( Matcher<T> matcher ) {
return matcher;
}
private static PentahoMetadataDomainRepository createMetadataDomainRepository() throws Exception {
IUnifiedRepository repository = new FileSystemBackedUnifiedRepository( "target/test-classes/dsw" );
mp.defineInstance( IUnifiedRepository.class, repository );
Assert.assertNotNull( new RepositoryUtils( repository ).getFolder( "/etc/metadata", true, true, null ) );
Assert.assertNotNull( new RepositoryUtils( repository ).getFolder( "/etc/mondrian", true, true, null ) );
PentahoMetadataDomainRepository pentahoMetadataDomainRepository = new PentahoMetadataDomainRepository( repository );
return pentahoMetadataDomainRepository;
}
private Domain generateModel() {
Domain domain = null;
try {
DatabaseMeta database = new DatabaseMeta();
database.setDatabaseType( "Hypersonic" ); //$NON-NLS-1$
database.setAccessType( DatabaseMeta.TYPE_ACCESS_JNDI );
database.setDBName( "SampleData" ); //$NON-NLS-1$
database.setName( "SampleData" ); //$NON-NLS-1$
System.out.println( database.testConnection() );
TableModelerSource source = new TableModelerSource( database, "ORDERS", null ); //$NON-NLS-1$
domain = source.generateDomain();
List<OlapDimension> olapDimensions = new ArrayList<OlapDimension>();
OlapDimension dimension = new OlapDimension();
dimension.setName( "test" ); //$NON-NLS-1$
dimension.setTimeDimension( false );
olapDimensions.add( dimension );
domain.getLogicalModels().get( 1 ).setProperty( "olap_dimensions", olapDimensions ); //$NON-NLS-1$
} catch ( Exception e ) {
e.printStackTrace();
}
return domain;
}
public static class MockDatasourceMgmtService implements IDatasourceMgmtService {
@Override
public void init( IPentahoSession arg0 ) {
}
@Override
public String createDatasource( IDatabaseConnection arg0 ) throws DuplicateDatasourceException,
DatasourceMgmtServiceException {
return null;
}
@Override
public void deleteDatasourceById( String arg0 ) throws NonExistingDatasourceException, DatasourceMgmtServiceException {
}
@Override
public void deleteDatasourceByName( String arg0 ) throws NonExistingDatasourceException,
DatasourceMgmtServiceException {
}
@Override
public IDatabaseConnection getDatasourceById( String arg0 ) throws DatasourceMgmtServiceException {
return null;
}
@Override
public IDatabaseConnection getDatasourceByName( String arg0 ) throws DatasourceMgmtServiceException {
return null;
}
@Override
public List<String> getDatasourceIds() throws DatasourceMgmtServiceException {
return null;
}
@Override
public List<IDatabaseConnection> getDatasources() throws DatasourceMgmtServiceException {
return null;
}
@Override
public String updateDatasourceById( String arg0, IDatabaseConnection arg1 ) throws NonExistingDatasourceException,
DatasourceMgmtServiceException {
return null;
}
@Override
public String updateDatasourceByName( String arg0, IDatabaseConnection arg1 ) throws NonExistingDatasourceException,
DatasourceMgmtServiceException {
return null;
}
}
public static class MockUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername( String name ) throws UsernameNotFoundException, DataAccessException {
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
authList.add( new SimpleGrantedAuthority( "Authenticated" ) );
authList.add( new SimpleGrantedAuthority( "Administrator" ) );
UserDetails user = new User( name, "password", true, true, true, true, authList );
return user;
}
}
public static class TestFileSystemBackedUnifiedRepository extends FileSystemBackedUnifiedRepository {
public TestFileSystemBackedUnifiedRepository() {
super( "bin/test-solutions/solution" );
}
}
public static class MockClientRepositoryPathsStrategy implements IClientRepositoryPathsStrategy {
@Override
public String getEtcFolderName() {
return null;
}
@Override
public String getEtcFolderPath() {
return null;
}
@Override
public String getHomeFolderName() {
return null;
}
@Override
public String getHomeFolderPath() {
return null;
}
@Override
public String getPublicFolderName() {
return null;
}
@Override
public String getPublicFolderPath() {
return null;
}
@Override
public String getRootFolderPath() {
return null;
}
@Override
public String getUserHomeFolderName( String arg0 ) {
return null;
}
@Override
public String getUserHomeFolderPath( String arg0 ) {
return null;
}
}
}