/*! * 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-2017 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.engine.classic.extensions.datasources.pmd; import org.pentaho.metadata.model.Domain; import org.pentaho.metadata.model.IPhysicalColumn; import org.pentaho.metadata.model.IPhysicalTable; import org.pentaho.metadata.model.SqlPhysicalModel; import org.pentaho.metadata.model.concept.types.AggregationType; import org.pentaho.metadata.model.concept.types.LocalizedString; import org.pentaho.metadata.repository.IMetadataDomainRepository; import org.pentaho.metadata.repository.InMemoryMetadataDomainRepository; import org.pentaho.metadata.util.XmiParser; import org.pentaho.reporting.engine.classic.core.DataFactory; import org.pentaho.reporting.engine.classic.core.MetaAttributeNames; import org.pentaho.reporting.engine.classic.core.ParameterDataRow; import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException; import org.pentaho.reporting.engine.classic.core.StaticDataRow; import org.pentaho.reporting.engine.classic.core.designtime.datafactory.DesignTimeDataFactoryContext; import org.pentaho.reporting.engine.classic.core.metadata.DataFactoryMetaData; import org.pentaho.reporting.engine.classic.core.testsupport.DataSourceTestBase; import org.pentaho.reporting.engine.classic.core.util.CloseableTableModel; import org.pentaho.reporting.engine.classic.core.wizard.ConceptQueryMapper; import org.pentaho.reporting.engine.classic.core.wizard.DataAttributes; import org.pentaho.reporting.engine.classic.core.wizard.DataSchema; import org.pentaho.reporting.engine.classic.core.wizard.DataSchemaCompiler; import org.pentaho.reporting.engine.classic.core.wizard.DefaultConceptQueryMapper; import org.pentaho.reporting.engine.classic.core.wizard.DefaultDataAttributeContext; import org.pentaho.reporting.engine.classic.core.wizard.DefaultDataSchemaDefinition; import org.pentaho.reporting.libraries.resourceloader.ResourceKey; import org.pentaho.reporting.libraries.resourceloader.ResourceManager; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** * @noinspection HardCodedStringLiteral */ public class PentahoMetaDataTest extends DataSourceTestBase { private static class MultipleAggregationTestConnectionProvider extends PmdConnectionProvider { private static final long serialVersionUID = 2672461111722673121L; public IMetadataDomainRepository getMetadataDomainRepository( final String domainId, final ResourceManager resourceManager, final ResourceKey contextKey, final String xmiFile ) throws ReportDataFactoryException { try { final InputStream stream = createStream( resourceManager, contextKey, xmiFile ); try { final InMemoryMetadataDomainRepository repo = new InMemoryMetadataDomainRepository(); final XmiParser parser = new XmiParser(); final Domain domain = parser.parseXmi( stream ); // add a couple of agg types to the quantity ordered physical column final IPhysicalTable table = ( (SqlPhysicalModel) domain.getPhysicalModels().get( 0 ) ).getPhysicalTables().get( 7 ); final IPhysicalColumn col = table.getPhysicalColumns().get( 3 ); final List<AggregationType> list = new ArrayList<AggregationType>(); list.add( AggregationType.SUM ); list.add( AggregationType.AVERAGE ); col.setAggregationList( list ); domain.setId( domainId ); repo.storeDomain( domain, true ); return repo; } finally { stream.close(); } } catch ( final Exception e ) { throw new ReportDataFactoryException( "The Specified XMI File is invalid: " + xmiFile, e ); } } } private static final String QUERY = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<mql>" + " <domain_type>relational</domain_type>" + " <domain_id>steel-wheels</domain_id>" + " <model_id>BV_HUMAN_RESOURCES</model_id>" + " <model_name>Human Resources</model_name>" + " <options>" + " <disable_distinct>true</disable_distinct>" + " </options>" + " <selections>" + " <selection>" + " <view>BC_EMPLOYEES_</view>" + " <column>BC_EMPLOYEES_FIRSTNAME</column>" + " </selection>" + " <selection>" + " <view>BC_EMPLOYEES_</view>" + " <column>BC_EMPLOYEES_LASTNAME</column>" + " </selection>" + " <selection>" + " <view>BC_EMPLOYEES_</view>" + " <column>BC_EMPLOYEES_EMPLOYEENUMBER</column>" + " </selection>" + " <selection>" + " <view>BC_EMPLOYEES_</view>" + " <column>BC_EMPLOYEES_EMAIL</column>" + " </selection>" + " </selections>" + " <constraints/>" + " <orders>" + " <order>" + " <direction>asc</direction>" + " <view_id>BC_OFFICES_</view_id>" + " <column_id>BC_OFFICES_COUNTRY</column_id>" + " </order>" + " <order>" + " <direction>asc</direction>" + " <view_id>BC_OFFICES_</view_id>" + " <column_id>BC_OFFICES_STATE</column_id>" + " </order>" + " </orders>" + "</mql>"; private static final String PARAMETRIZED_QUERY = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<mql>" + "<domain_id>steel-wheels</domain_id>" + "<model_id>BV_ORDERS</model_id>" + "<options>" + "<disable_distinct>false</disable_distinct>" + "</options>" + "<parameters>" + "<parameter defaultValue=\"Shipped\" name=\"oStatus\" type=\"STRING\"/>" + "</parameters>" + "<selections>" + "<selection>" + "<view>BC_CUSTOMER_W_TER_</view>" + "<column>BC_CUSTOMER_W_TER_CUSTOMERNUMBER</column>" + "<aggregation>NONE</aggregation>" + "</selection>" + "<selection>" + "<view>BC_CUSTOMER_W_TER_</view>" + "<column>BC_CUSTOMER_W_TER_CUSTOMERNAME</column>" + "<aggregation>NONE</aggregation>" + "</selection>" + "<selection>" + "<view>CAT_ORDERS</view>" + "<column>BC_ORDERS_ORDERNUMBER</column>" + "<aggregation>NONE</aggregation>" + "</selection>" + "<selection>" + "<view>CAT_ORDERS</view>" + "<column>BC_ORDERDETAILS_TOTAL</column>" + "<aggregation>SUM</aggregation>" + "</selection>" + "<selection>" + "<view>CAT_ORDERS</view>" + "<column>BC_ORDERS_STATUS</column>" + "<aggregation>NONE</aggregation>" + "</selection>" + "<selection>" + "<view>CAT_ORDERS</view>" + "<column>BC_ORDERS_COMMENTS</column>" + "<aggregation>NONE</aggregation>" + "</selection>" + "</selections>" + "<constraints>" + "<constraint>" + "<operator/>" + "<condition>[CAT_ORDERS.BC_ORDERS_STATUS] = [param:oStatus]</condition>" + "</constraint>" + "</constraints>" + "<orders/>" + "</mql>"; private static final String MULTIPLE_AGG_QUERY = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<mql>" + "<domain_id>steel-wheels</domain_id>" + "<model_id>BV_ORDERS</model_id>" + "<options>" + "<disable_distinct>false</disable_distinct>" + "</options>" + "<selections>" + "<selection>" + "<view>BC_CUSTOMER_W_TER_</view>" + "<column>BC_CUSTOMER_W_TER_TERRITORY</column>" + "<aggregation>NONE</aggregation>" + "</selection>" + "<selection>" + "<view>CAT_ORDERS</view>" + "<column>BC_ORDERDETAILS_QUANTITYORDERED</column>" + "<aggregation>SUM</aggregation>" + "</selection>" + "<selection>" + "<view>CAT_ORDERS</view>" + "<column>BC_ORDERDETAILS_QUANTITYORDERED</column>" + "<aggregation>AVERAGE</aggregation>" + "</selection>" + "</selections>" + "<constraints>" + "</constraints>" + "<orders/>" + "</mql>"; public static final String[][] QUERIES_AND_RESULTS = new String[][] { { QUERY, "query-results.txt", "design-time-query-results.txt" }, // {PARAMETRIZED_QUERY, "query-results-2.txt", "design-time-query-results-2.txt"}, }; public PentahoMetaDataTest() { } protected DataFactory createDataFactory( final String query ) throws ReportDataFactoryException { final PmdDataFactory pmdDataFactory = new PmdDataFactory(); pmdDataFactory.setConnectionProvider( new PmdConnectionProvider() ); pmdDataFactory.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory.setDomainId( "steel-wheels" ); pmdDataFactory.setQuery( "default", query, null, null ); initializeDataFactory( pmdDataFactory ); return pmdDataFactory; } public void testDataSchemaCompiler() throws Exception { final PmdDataFactory pmdDataFactory = new PmdDataFactory(); pmdDataFactory.setConnectionProvider( new PmdConnectionProvider() ); pmdDataFactory.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory.setDomainId( "steel-wheels" ); pmdDataFactory.initialize( new DesignTimeDataFactoryContext() ); try { pmdDataFactory.setQuery( "default", QUERY, null, null ); final CloseableTableModel tableModel = (CloseableTableModel) pmdDataFactory.queryData( "default", new ParameterDataRow() ); try { final DefaultDataSchemaDefinition def = new DefaultDataSchemaDefinition(); final DataSchemaCompiler compiler = new DataSchemaCompiler( def, new DefaultDataAttributeContext() ); final DataSchema dataSchema = compiler.compile( tableModel ); final String[] names = dataSchema.getNames(); assertEquals( 4, names.length ); assertEquals( "BC_EMPLOYEES_FIRSTNAME", names[ 0 ] ); assertEquals( "BC_EMPLOYEES_LASTNAME", names[ 1 ] ); assertEquals( "BC_EMPLOYEES_EMPLOYEENUMBER", names[ 2 ] ); assertEquals( "BC_EMPLOYEES_EMAIL", names[ 3 ] ); final DataAttributes attributes = dataSchema.getAttributes( names[ 2 ] ); // assert that formatting-label is not a default mapper final ConceptQueryMapper mapper = attributes.getMetaAttributeMapper( MetaAttributeNames.Formatting.NAMESPACE, MetaAttributeNames.Formatting.LABEL ); if ( mapper instanceof DefaultConceptQueryMapper ) { fail( "Formatting::label should be a LocalizedString instead of a default-mapper" ); } final Object value = attributes.getMetaAttribute( MetaAttributeNames.Formatting.NAMESPACE, MetaAttributeNames.Formatting.LABEL, null, new DefaultDataAttributeContext() ); if ( value instanceof LocalizedString == false ) { fail( "Formatting::label should be a LocalizedString" ); } final Object label = attributes.getMetaAttribute( MetaAttributeNames.Formatting.NAMESPACE, MetaAttributeNames.Formatting.LABEL, String.class, new DefaultDataAttributeContext( Locale.US ) ); if ( label instanceof String == false ) { fail( "Formatting::label should be a String" ); } final Object elementAlignment = attributes.getMetaAttribute( MetaAttributeNames.Style.NAMESPACE, MetaAttributeNames.Style.HORIZONTAL_ALIGNMENT, null, new DefaultDataAttributeContext( Locale.US ) ); if ( "right".equals( elementAlignment ) == false ) { fail( "Style::horizontal-alignment should be a String of value 'right'" ); } final DataAttributes attributes2 = dataSchema.getAttributes( names[ 0 ] ); final Object elementAlignment2 = attributes2.getMetaAttribute( MetaAttributeNames.Style.NAMESPACE, MetaAttributeNames.Style.HORIZONTAL_ALIGNMENT, null, new DefaultDataAttributeContext( Locale.US ) ); if ( "left".equals( elementAlignment2 ) == false ) { fail( "Style::horizontal-alignment should be a String of value 'right'" ); } } finally { tableModel.close(); } } finally { pmdDataFactory.close(); } } public void testMetaData() throws ReportDataFactoryException { final PmdDataFactory pmdDataFactory = new PmdDataFactory(); pmdDataFactory.setConnectionProvider( new PmdConnectionProvider() ); pmdDataFactory.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory.setDomainId( "steel-wheels" ); pmdDataFactory.initialize( new DesignTimeDataFactoryContext() ); pmdDataFactory.setQuery( "default", PARAMETRIZED_QUERY, null, null ); final DataFactoryMetaData metaData = pmdDataFactory.getMetaData(); final Object queryHash = metaData.getQueryHash( pmdDataFactory, "default", new StaticDataRow() ); assertNotNull( queryHash ); final PmdDataFactory pmdDataFactory2 = new PmdDataFactory(); pmdDataFactory2.setConnectionProvider( new PmdConnectionProvider() ); pmdDataFactory2.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory2.setDomainId( "steel-wheels" ); pmdDataFactory2.initialize( new DesignTimeDataFactoryContext() ); pmdDataFactory2.setQuery( "default", QUERY, null, null ); pmdDataFactory2.setQuery( "default2", PARAMETRIZED_QUERY, null, null ); assertNotEquals( "Physical Query is not the same", queryHash, metaData.getQueryHash( pmdDataFactory2, "default", new StaticDataRow() ) ); assertEquals( "Physical Query is the same", queryHash, metaData.getQueryHash( pmdDataFactory2, "default2", new StaticDataRow() ) ); final PmdDataFactory pmdDataFactory3 = new PmdDataFactory(); pmdDataFactory3.setConnectionProvider( new PmdConnectionProvider() ); pmdDataFactory3.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory3.setDomainId( "steel-wheels2" ); pmdDataFactory3.setQuery( "default", QUERY, null, null ); pmdDataFactory3.setQuery( "default2", PARAMETRIZED_QUERY, null, null ); assertNotEquals( "Physical Connection is not the same", queryHash, metaData.getQueryHash( pmdDataFactory3, "default", new StaticDataRow() ) ); assertNotEquals( "Physical Connection is the same", queryHash, metaData.getQueryHash( pmdDataFactory3, "default2", new StaticDataRow() ) ); final PmdDataFactory pmdDataFactory4 = new PmdDataFactory(); pmdDataFactory4.setConnectionProvider( new PmdConnectionProvider() ); pmdDataFactory4.setXmiFile( "src/test/resources/metadata/metadata2.xmi" ); pmdDataFactory4.setDomainId( "steel-wheels" ); pmdDataFactory4.setQuery( "default", QUERY, null, null ); pmdDataFactory4.setQuery( "default2", PARAMETRIZED_QUERY, null, null ); assertNotEquals( "Physical Connection is not the same", queryHash, metaData.getQueryHash( pmdDataFactory4, "default", new StaticDataRow() ) ); assertNotEquals( "Physical Connection is the same", queryHash, metaData.getQueryHash( pmdDataFactory4, "default2", new StaticDataRow() ) ); } public void testMultipleAggregations() throws Exception { final PmdDataFactory pmdDataFactory = new PmdDataFactory(); pmdDataFactory.setConnectionProvider( new MultipleAggregationTestConnectionProvider() ); pmdDataFactory.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory.setDomainId( "steel-wheels" ); pmdDataFactory.initialize( new DesignTimeDataFactoryContext() ); try { pmdDataFactory.setQuery( "default", MULTIPLE_AGG_QUERY, null, null ); final CloseableTableModel tableModel = (CloseableTableModel) pmdDataFactory.queryData( "default", new ParameterDataRow() ); try { final DefaultDataSchemaDefinition def = new DefaultDataSchemaDefinition(); final DataSchemaCompiler compiler = new DataSchemaCompiler( def, new DefaultDataAttributeContext() ); final DataSchema dataSchema = compiler.compile( tableModel ); final String[] names = dataSchema.getNames(); assertEquals( 3, names.length ); assertEquals( "BC_CUSTOMER_W_TER_TERRITORY", names[ 0 ] ); assertEquals( "BC_ORDERDETAILS_QUANTITYORDERED", names[ 1 ] ); assertEquals( "BC_ORDERDETAILS_QUANTITYORDERED:AVERAGE", names[ 2 ] ); final ByteArrayOutputStream sw = new ByteArrayOutputStream(); final PrintStream out = new PrintStream( sw ); generateCompareText( out, tableModel ); compareLineByLine( "agg-query-results.txt", sw.toString() ); } finally { tableModel.close(); } } finally { pmdDataFactory.close(); } } public void runGenerateMultiAgg() throws ReportDataFactoryException, IOException, SQLException { final PmdDataFactory pmdDataFactory = new PmdDataFactory(); pmdDataFactory.setConnectionProvider( new MultipleAggregationTestConnectionProvider() ); pmdDataFactory.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory.setDomainId( "steel-wheels" ); pmdDataFactory.setQuery( "default", MULTIPLE_AGG_QUERY, null, null ); pmdDataFactory.initialize( new DesignTimeDataFactoryContext() ); generate( pmdDataFactory, "agg-query-results.txt" ); generateDesignTime( pmdDataFactory, "design-time-agg-query-results.txt" ); } public void testParameter() throws ReportDataFactoryException { final PmdDataFactory pmdDataFactory = new PmdDataFactory(); pmdDataFactory.setConnectionProvider( new PmdConnectionProvider() ); pmdDataFactory.setXmiFile( "src/test/resources/metadata/metadata.xmi" ); pmdDataFactory.setDomainId( "steel-wheels" ); pmdDataFactory.initialize( new DesignTimeDataFactoryContext() ); pmdDataFactory.setQuery( "default", PARAMETRIZED_QUERY, null, null ); pmdDataFactory.setQuery( "default2", QUERY, null, null ); final DataFactoryMetaData metaData = pmdDataFactory.getMetaData(); final String[] fields = metaData.getReferencedFields( pmdDataFactory, "default", new StaticDataRow() ); assertNotNull( fields ); assertEquals( 3, fields.length ); assertEquals( "oStatus", fields[ 0 ] ); assertEquals( DataFactory.QUERY_LIMIT, fields[ 1 ] ); assertEquals( DataFactory.QUERY_TIMEOUT, fields[ 2 ] ); final String[] fields2 = metaData.getReferencedFields( pmdDataFactory, "default2", new StaticDataRow() ); assertNotNull( fields2 ); assertEquals( 2, fields2.length ); assertEquals( DataFactory.QUERY_LIMIT, fields2[ 0 ] ); assertEquals( DataFactory.QUERY_TIMEOUT, fields2[ 1 ] ); } public void testSaveAndLoad() throws Exception { runSaveAndLoad( QUERIES_AND_RESULTS ); } public void testDerive() throws Exception { runDerive( QUERIES_AND_RESULTS ); } public void testSerialize() throws Exception { runSerialize( QUERIES_AND_RESULTS ); } public void testQuery() throws Exception { runTest( QUERIES_AND_RESULTS ); } }