/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache 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://www.apache.org/licenses/LICENSE-2.0 * * 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.apache.lens.server.query; import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.NOT_FOUND; import static org.apache.lens.api.error.LensCommonErrorCode.INTERNAL_SERVER_ERROR; import static org.apache.lens.cube.error.LensCubeErrorCode.COLUMN_UNAVAILABLE_IN_TIME_RANGE; import static org.apache.lens.cube.error.LensCubeErrorCode.SYNTAX_ERROR; import static org.apache.lens.server.common.RestAPITestUtil.*; import static org.apache.lens.server.common.TestDataUtils.*; import static org.apache.lens.server.error.LensServerErrorCode.*; import static org.testng.Assert.assertTrue; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.xml.datatype.DatatypeConfigurationException; import org.apache.lens.api.LensConf; import org.apache.lens.api.LensSessionHandle; import org.apache.lens.api.SupportedOperations; import org.apache.lens.api.jaxb.LensJAXBContextResolver; import org.apache.lens.api.metastore.*; import org.apache.lens.api.result.LensAPIResult; import org.apache.lens.api.result.LensErrorTO; import org.apache.lens.api.util.MoxyJsonConfigurationContextResolver; import org.apache.lens.cube.error.ColUnAvailableInTimeRange; import org.apache.lens.server.LensJerseyTest; import org.apache.lens.server.LensRequestLoggingFilter; import org.apache.lens.server.common.ErrorResponseExpectedData; import org.apache.lens.server.error.GenericExceptionMapper; import org.apache.lens.server.error.LensJAXBValidationExceptionMapper; import org.apache.lens.server.metastore.MetastoreResource; import org.apache.lens.server.session.SessionResource; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.moxy.json.MoxyJsonFeature; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.TestProperties; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.common.base.Optional; import lombok.NonNull; @Test(groups = "unit-test") public class QueryAPIErrorResponseTest extends LensJerseyTest { private static final String MOCK_QUERY = "mock-query"; private static final String INVALID_OPERATION = "invalid-operation"; @BeforeTest public void setUp() throws Exception { super.setUp(); } @AfterTest public void tearDown() throws Exception { super.tearDown(); } @Override protected Application configure() { enable(TestProperties.LOG_TRAFFIC); enable(TestProperties.DUMP_ENTITY); return new ResourceConfig(LensRequestLoggingFilter.class, SessionResource.class, MetastoreResource.class, QueryServiceResource.class, MultiPartFeature.class, GenericExceptionMapper.class, LensJAXBContextResolver.class, LensRequestLoggingFilter.class, LensJAXBValidationExceptionMapper.class, MoxyJsonConfigurationContextResolver.class, MoxyJsonFeature.class); } @Test(dataProvider = "mediaTypeData") public void testErrorResponseWhenSessionIdIsAbsent(MediaType mt) { Response response = estimate(target(), Optional.<LensSessionHandle>absent(), Optional.of(MOCK_QUERY), mt); final String expectedErrMsg = "Session id not provided. Please provide a session id."; LensErrorTO expectedLensErrorTO = LensErrorTO.composedOf( SESSION_ID_NOT_PROVIDED.getLensErrorInfo().getErrorCode(), expectedErrMsg, MOCK_STACK_TRACE); ErrorResponseExpectedData expectedData = new ErrorResponseExpectedData(BAD_REQUEST, expectedLensErrorTO); expectedData.verify(response); } @Test(dataProvider = "mediaTypeData") public void testErrorResponseWhenQueryIsAbsent(MediaType mt) { LensSessionHandle sessionId = openSession(target(), "foo", "bar", new LensConf(), mt); Optional<String> testQuery = Optional.absent(); Response response = estimate(target(), Optional.of(sessionId), testQuery, mt); final String expectedErrMsg = "Query is not provided, or it is empty or blank. Please provide a valid query."; LensErrorTO expectedLensErrorTO = LensErrorTO.composedOf( NULL_OR_EMPTY_OR_BLANK_QUERY.getLensErrorInfo().getErrorCode(), expectedErrMsg, MOCK_STACK_TRACE); ErrorResponseExpectedData expectedData = new ErrorResponseExpectedData(BAD_REQUEST, expectedLensErrorTO); expectedData.verify(response); closeSession(target(), sessionId, mt); } @Test(dataProvider = "mediaTypeData") public void testErrorResponseWhenInvalidOperationIsSubmitted(MediaType mt) { LensSessionHandle sessionId = openSession(target(), "foo", "bar", new LensConf(), mt); Response response = postQuery(target(), Optional.of(sessionId), Optional.of(MOCK_QUERY), Optional.of(INVALID_OPERATION), mt); final String expectedErrMsg = "Provided Operation is not supported. Supported Operations are: " + "[estimate, execute, explain, execute_with_timeout]"; LensErrorTO expectedLensErrorTO = LensErrorTO.composedOf( UNSUPPORTED_OPERATION.getLensErrorInfo().getErrorCode(), expectedErrMsg, MOCK_STACK_TRACE, new SupportedOperations()); ErrorResponseExpectedData expectedData = new ErrorResponseExpectedData(BAD_REQUEST, expectedLensErrorTO); expectedData.verify(response); closeSession(target(), sessionId, mt); } @Test(dataProvider = "mediaTypeData") public void testErrorResponseWhenLensMultiCauseExceptionOccurs(MediaType mt) { LensSessionHandle sessionId = openSession(target(), "foo", "bar", mt); final String testQuery = "select * from non_existing_table"; Response response = estimate(target(), Optional.of(sessionId), Optional.of(testQuery), mt); final String expectedErrMsg1 = "Semantic Error : Error while compiling statement: " + "FAILED: SemanticException [Error 10001]: Line 1:31 Table not found 'non_existing_table'"; final String expectedErrMsg2 = "Semantic Error : user lacks privilege or object not found: NON_EXISTING_TABLE"; LensErrorTO expectedLensErrorTO1 = LensErrorTO.composedOf(INTERNAL_SERVER_ERROR.getValue(), expectedErrMsg1, MOCK_STACK_TRACE); LensErrorTO expectedLensErrorTO2 = LensErrorTO.composedOf(INTERNAL_SERVER_ERROR.getValue(), expectedErrMsg2, MOCK_STACK_TRACE); LensErrorTO responseLensErrorTO = response.readEntity(LensAPIResult.class).getLensErrorTO(); assertTrue(expectedLensErrorTO1.getMessage().equals(responseLensErrorTO.getMessage()) || expectedLensErrorTO2.getMessage().equals(responseLensErrorTO.getMessage()), "Message is " + responseLensErrorTO.getMessage()); closeSession(target(), sessionId, mt); } @Test(dataProvider = "mediaTypeData") public void testErrorResponseWithSyntaxErrorInQuery(MediaType mt) { LensSessionHandle sessionId = openSession(target(), "foo", "bar", new LensConf(), mt); Response response = estimate(target(), Optional.of(sessionId), Optional.of(MOCK_QUERY), mt); final String expectedErrMsg = "Syntax Error: line 1:0 cannot recognize input near 'mock' '-' 'query'"; LensErrorTO expectedLensErrorTO = LensErrorTO.composedOf(SYNTAX_ERROR.getLensErrorInfo().getErrorCode(), expectedErrMsg, MOCK_STACK_TRACE); ErrorResponseExpectedData expectedData = new ErrorResponseExpectedData(BAD_REQUEST, expectedLensErrorTO); expectedData.verify(response); closeSession(target(), sessionId, mt); } @Test(dataProvider = "mediaTypeData") public void testQueryColumnWithBothStartDateAndEndDate(MediaType mt) throws DatatypeConfigurationException { /* This test will have a col which has both start date and end date set */ /* Col will be queried for a time range which does not fall in start date and end date */ DateTime startDateOneJan2015 = new DateTime(2015, 01, 01, 0, 0, DateTimeZone.UTC); DateTime endDateThirtyJan2015 = new DateTime(2015, 01, 30, 23, 0, DateTimeZone.UTC); DateTime queryFromOneJan2014 = new DateTime(2014, 01, 01, 0, 0, DateTimeZone.UTC); DateTime queryTillThreeJan2014 = new DateTime(2014, 01, 03, 0, 0, DateTimeZone.UTC); final String expectedErrMsgSuffix = " can only be queried after Thursday, January 1, 2015 12:00:00 AM UTC and " + "before Friday, January 30, 2015 11:00:00 PM UTC. Please adjust the selected time range accordingly."; testColUnAvailableInTimeRange(Optional.of(startDateOneJan2015), Optional.of(endDateThirtyJan2015), queryFromOneJan2014, queryTillThreeJan2014, expectedErrMsgSuffix, mt); } @Test(dataProvider = "mediaTypeData") public void testQueryColumnWithOnlyStartDate(MediaType mt) throws DatatypeConfigurationException { /* This test will have a col which has only start date set */ /* Col will be queried for a time range which is before start date */ DateTime startDateOneJan2015 = new DateTime(2015, 01, 01, 0, 0, DateTimeZone.UTC); DateTime queryFromOneJan2014 = new DateTime(2014, 01, 01, 0, 0, DateTimeZone.UTC); DateTime queryTillThreeJan2014 = new DateTime(2014, 01, 03, 0, 0, DateTimeZone.UTC); final String expectedErrMsgSuffix = " can only be queried after Thursday, January 1, 2015 12:00:00 AM UTC. " + "Please adjust the selected time range accordingly."; testColUnAvailableInTimeRange(Optional.of(startDateOneJan2015), Optional.<DateTime>absent(), queryFromOneJan2014, queryTillThreeJan2014, expectedErrMsgSuffix, mt); } @Test(dataProvider = "mediaTypeData") public void testQueryColumnWithOnlyEndDate(MediaType mt) throws DatatypeConfigurationException { /* This test will have a col which has only end date set */ /* Col will be queried for a time range which is after end date */ DateTime endDateThirtyJan2015 = new DateTime(2015, 01, 30, 23, 0, DateTimeZone.UTC); DateTime queryFromOneJan2016 = new DateTime(2016, 01, 01, 0, 0, DateTimeZone.UTC); DateTime queryTillThreeJan2016 = new DateTime(2016, 01, 03, 0, 0, DateTimeZone.UTC); final String expectedErrMsgSuffix = " can only be queried before Friday, January 30, 2015 11:00:00 PM UTC. " + "Please adjust the selected time range accordingly."; testColUnAvailableInTimeRange(Optional.<DateTime>absent(), Optional.of(endDateThirtyJan2015), queryFromOneJan2016, queryTillThreeJan2016, expectedErrMsgSuffix, mt); } private void testColUnAvailableInTimeRange(@NonNull final Optional<DateTime> colStartDate, @NonNull final Optional<DateTime> colEndDate, @NonNull DateTime queryFrom, @NonNull DateTime queryTill, @NonNull final String expectedErrorMsgSuffix, @NonNull final MediaType mt) throws DatatypeConfigurationException { final WebTarget target = target(); final String testDb = getRandomDbName(); final String testCube = getRandomCubeName(); final String testDimensionField = getRandomDimensionField(); final String testFact = getRandomFactName(); /* Setup: Begin */ LensSessionHandle sessionId = openSession(target, "foo", "bar", new LensConf(), mt); try { createAndSetCurrentDbFailFast(target, sessionId, testDb, mt); /* Create a test cube with test dimension field having a start Date and end Date */ XDimAttribute testXDim = createXDimAttribute(testDimensionField, colStartDate, colEndDate); XCube xcube = createXCubeWithDummyMeasure(testCube, Optional.of("dt"), testXDim); createCubeFailFast(target, sessionId, xcube, mt); /* Create a fact with test dimension field */ XColumn xColumn = createXColumn(testDimensionField); XFactTable xFactTable = createXFactTableWithColumns(testFact, testCube, xColumn); createFactFailFast(target, sessionId, xFactTable, mt); /* Setup: End */ DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd-HH"); final String testQuery = "cube select " + testDimensionField + " from " + testCube + " where TIME_RANGE_IN(dt, " + "\"" + dtf.print(queryFrom) + "\",\"" + dtf.print(queryTill) + "\")"; Response response = estimate(target, Optional.of(sessionId), Optional.of(testQuery), mt); final String expectedErrMsg = testDimensionField + expectedErrorMsgSuffix; Long expecAvailableFrom = colStartDate.isPresent() ? colStartDate.get().getMillis() : null; Long expecAvailableTill = colEndDate.isPresent() ? colEndDate.get().getMillis() : null; final ColUnAvailableInTimeRange expectedErrorPayload = new ColUnAvailableInTimeRange(testDimensionField, expecAvailableFrom, expecAvailableTill); LensErrorTO expectedLensErrorTO = LensErrorTO.composedOf( COLUMN_UNAVAILABLE_IN_TIME_RANGE.getLensErrorInfo().getErrorCode(), expectedErrMsg, MOCK_STACK_TRACE, expectedErrorPayload, null); ErrorResponseExpectedData expectedData = new ErrorResponseExpectedData(BAD_REQUEST, expectedLensErrorTO); expectedData.verify(response); } finally { dropDatabaseFailFast(target, sessionId, testDb, mt); closeSessionFailFast(target, sessionId, mt); } } /** * Test execute failure in with selected driver throwing Runtime exception. * * @throws InterruptedException the interrupted exception */ @Test(dataProvider = "mediaTypeData") public void testExplainRuntimeException(MediaType mt) throws InterruptedException { LensSessionHandle sessionId = openSession(target(), "foo", "bar", new LensConf(), mt); try { Response response = explain(target(), Optional.of(sessionId), Optional.of("select fail, execute_runtime " + " from non_exist"), mt); final String expectedErrMsg = "Internal server error:Runtime exception from query explain"; LensErrorTO expectedLensErrorTO = LensErrorTO.composedOf( INTERNAL_SERVER_ERROR.getValue(), expectedErrMsg, MOCK_STACK_TRACE); ErrorResponseExpectedData expectedData = new ErrorResponseExpectedData(Response.Status.INTERNAL_SERVER_ERROR, expectedLensErrorTO); expectedData.verify(response); } finally { closeSessionFailFast(target(), sessionId, mt); } } /** * Test execute failure in with selected driver throwing webapp exception. * * @throws InterruptedException the interrupted exception */ @Test(dataProvider = "mediaTypeData") public void testExplainWebappException(MediaType mt) throws InterruptedException { LensSessionHandle sessionId = openSession(target(), "foo", "bar", new LensConf(), mt); try { Response response = explain(target(), Optional.of(sessionId), Optional.of("select fail, webappexception " + " from non_exist"), mt); final String expectedErrMsg = "Not found from mock driver"; LensErrorTO expectedLensErrorTO = LensErrorTO.composedOf( NOT_FOUND.getStatusCode(), expectedErrMsg, MOCK_STACK_TRACE); ErrorResponseExpectedData expectedData = new ErrorResponseExpectedData(Response.Status.NOT_FOUND, expectedLensErrorTO); expectedData.verify(response); } finally { closeSessionFailFast(target(), sessionId, mt); } } }