This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch camel-2.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit 12ca5a537e3dfc0eb9b2b02782016f5e15e80991 Author: Christoph Deppisch <cdeppi...@redhat.com> AuthorDate: Thu Jun 6 12:47:51 2019 +0200 CAMEL-13617: Improve testability of google-sheets component --- components/camel-google-sheets/pom.xml | 90 ++++- .../main/docs/google-sheets-stream-component.adoc | 2 +- .../sheets/BatchGoogleSheetsClientFactory.java | 63 +++- .../google/sheets/GoogleSheetsClientFactory.java | 6 +- .../google/sheets/GoogleSheetsEndpoint.java | 41 ++- .../google/sheets/GoogleSheetsProducer.java | 2 +- .../sheets/GoogleSheetsVerifierExtension.java | 9 +- .../sheets/internal/GoogleSheetsConstants.java | 4 +- .../stream/GoogleSheetsStreamConfiguration.java | 4 +- .../sheets/stream/GoogleSheetsStreamConsumer.java | 23 +- .../sheets/AbstractGoogleSheetsTestSupport.java | 43 ++- .../sheets/SheetsSpreadsheetsIntegrationTest.java | 37 +- .../SheetsSpreadsheetsValuesIntegrationTest.java | 83 ++++- .../sheets/server/GoogleSheetsApiTestServer.java | 354 ++++++++++++++++++ .../server/GoogleSheetsApiTestServerAssert.java | 405 +++++++++++++++++++++ .../server/GoogleSheetsApiTestServerRule.java | 116 ++++++ .../AbstractGoogleSheetsStreamTestSupport.java | 17 + .../SheetsStreamConsumerIntegrationTest.java | 77 +++- .../src/test/resources/googleapis.jks | Bin 0 -> 2695 bytes .../src/test/resources/test-options.properties | 8 +- parent/pom.xml | 1 + .../GoogleSheetsStreamComponentConfiguration.java | 2 +- 22 files changed, 1281 insertions(+), 106 deletions(-) diff --git a/components/camel-google-sheets/pom.xml b/components/camel-google-sheets/pom.xml index d13d94f..0be3151 100644 --- a/components/camel-google-sheets/pom.xml +++ b/components/camel-google-sheets/pom.xml @@ -29,8 +29,8 @@ <artifactId>camel-google-sheets</artifactId> <packaging>jar</packaging> - <name>Camel :: GoogleSheets</name> - <description>Camel Component for GoogleSheets</description> + <name>Camel :: Google Sheets</name> + <description>Camel Component for Google Sheets</description> <properties> <schemeName>google-sheets</schemeName> @@ -40,8 +40,53 @@ <camel.osgi.private.pkg>org.apache.camel.component.google.sheets.internal</camel.osgi.private.pkg> <camel.osgi.export.pkg>org.apache.camel.component.google.sheets</camel.osgi.export.pkg> <camel.osgi.export.service>org.apache.camel.spi.ComponentResolver;component=google-sheets</camel.osgi.export.service> + <spring-security-oauth2-version>2.3.6.RELEASE</spring-security-oauth2-version> </properties> + <dependencyManagement> + <dependencies> + <!-- Test dependencies --> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>${assertj-version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-webmvc</artifactId> + <version>${spring-version}</version> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-web</artifactId> + <version>${spring-security-version}</version> + </dependency> + <dependency> + <groupId>org.springframework.security.oauth</groupId> + <artifactId>spring-security-oauth2</artifactId> + <version>${spring-security-oauth2-version}</version> + </dependency> + <dependency> + <groupId>com.consol.citrus</groupId> + <artifactId>citrus-core</artifactId> + <version>${citrus.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.consol.citrus</groupId> + <artifactId>citrus-java-dsl</artifactId> + <version>${citrus.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.consol.citrus</groupId> + <artifactId>citrus-http</artifactId> + <version>${citrus.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> <dependency> <groupId>org.apache.camel</groupId> @@ -78,9 +123,9 @@ <!-- Component API javadoc in provided scope to read API signatures --> <dependency> - <groupId>com.google.apis</groupId> - <artifactId>google-api-services-sheets</artifactId> - <version>${google-api-services-sheets-version}</version> + <groupId>com.google.apis</groupId> + <artifactId>google-api-services-sheets</artifactId> + <version>${google-api-services-sheets-version}</version> <type>javadoc</type> <scope>provided</scope> </dependency> @@ -108,6 +153,41 @@ <artifactId>camel-test</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.security.oauth</groupId> + <artifactId>spring-security-oauth2</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.consol.citrus</groupId> + <artifactId>citrus-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.consol.citrus</groupId> + <artifactId>citrus-java-dsl</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.consol.citrus</groupId> + <artifactId>citrus-http</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/components/camel-google-sheets/src/main/docs/google-sheets-stream-component.adoc b/components/camel-google-sheets/src/main/docs/google-sheets-stream-component.adoc index ac98fdc..9112d6d 100644 --- a/components/camel-google-sheets/src/main/docs/google-sheets-stream-component.adoc +++ b/components/camel-google-sheets/src/main/docs/google-sheets-stream-component.adoc @@ -91,7 +91,7 @@ with the following path and query parameters: | *clientSecret* (consumer) | Client secret of the sheets application | | String | *includeGridData* (consumer) | True if grid data should be returned. | false | boolean | *majorDimension* (consumer) | Specifies the major dimension that results should use.. | ROWS | String -| *maxResults* (consumer) | Specify the maximum number of returned results. This will limit the number of rows in a returned value range data set or the number of returned value ranges in a batch request. | 10 | int +| *maxResults* (consumer) | Specify the maximum number of returned results. This will limit the number of rows in a returned value range data set or the number of returned value ranges in a batch request. | 0 | int | *range* (consumer) | Specifies the range of rows and columns in a sheet to get data from. | | String | *refreshToken* (consumer) | OAuth 2 refresh token. Using this, the Google Calendar component can obtain a new accessToken whenever the current one expires - a necessity if the application is long-lived. | | String | *scopes* (consumer) | Specifies the level of permissions you want a sheets application to have to a user account. See https://developers.google.com/identity/protocols/googlescopes for more info. | | List diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/BatchGoogleSheetsClientFactory.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/BatchGoogleSheetsClientFactory.java index 1c5a91a..4125083 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/BatchGoogleSheetsClientFactory.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/BatchGoogleSheetsClientFactory.java @@ -18,49 +18,78 @@ package org.apache.camel.component.google.sheets; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.services.sheets.v4.Sheets; import org.apache.camel.RuntimeCamelException; +import org.apache.camel.util.ObjectHelper; public class BatchGoogleSheetsClientFactory implements GoogleSheetsClientFactory { - private final NetHttpTransport transport; + private final HttpTransport transport; private final JacksonFactory jsonFactory; public BatchGoogleSheetsClientFactory() { - this.transport = new NetHttpTransport(); - this.jsonFactory = new JacksonFactory(); + this(new NetHttpTransport(), new JacksonFactory()); + } + + public BatchGoogleSheetsClientFactory(HttpTransport httpTransport) { + this(httpTransport, new JacksonFactory()); + } + + public BatchGoogleSheetsClientFactory(HttpTransport httpTransport, JacksonFactory jacksonFactory) { + this.transport = httpTransport; + this.jsonFactory = jacksonFactory; } @Override - public Sheets makeClient(String clientId, String clientSecret, String applicationName, String refreshToken, String accessToken) { + public Sheets makeClient(String clientId, + String clientSecret, + String applicationName, + String refreshToken, + String accessToken) { if (clientId == null || clientSecret == null) { throw new IllegalArgumentException("clientId and clientSecret are required to create Google Sheets client."); } + try { - Credential credential = authorize(clientId, clientSecret); + Credential credential = authorize(clientId, clientSecret, refreshToken, accessToken); - if (refreshToken != null && !"".equals(refreshToken)) { - credential.setRefreshToken(refreshToken); - } - if (accessToken != null && !"".equals(accessToken)) { - credential.setAccessToken(accessToken); - } - return new Sheets.Builder(transport, jsonFactory, credential) - .setApplicationName(applicationName) - .build(); + Sheets.Builder clientBuilder = new Sheets.Builder(transport, jsonFactory, credential) + .setApplicationName(applicationName); + configure(clientBuilder); + return clientBuilder.build(); } catch (Exception e) { throw new RuntimeCamelException("Could not create Google Sheets client.", e); } } + /** + * Subclasses may add customized configuration to client builder. + * @param clientBuilder + */ + protected void configure(Sheets.Builder clientBuilder) { + clientBuilder.setRootUrl(Sheets.DEFAULT_ROOT_URL); + } + // Authorizes the installed application to access user's protected data. - private Credential authorize(String clientId, String clientSecret) { + private Credential authorize(String clientId, String clientSecret, String refreshToken, String accessToken) { // authorize - return new GoogleCredential.Builder() + Credential credential = new GoogleCredential.Builder() .setJsonFactory(jsonFactory) .setTransport(transport) - .setClientSecrets(clientId, clientSecret).build(); + .setClientSecrets(clientId, clientSecret) + .build(); + + if (ObjectHelper.isNotEmpty(refreshToken)) { + credential.setRefreshToken(refreshToken); + } + + if (ObjectHelper.isNotEmpty(accessToken)) { + credential.setAccessToken(accessToken); + } + + return credential; } } diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsClientFactory.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsClientFactory.java index 78b4e97..8186790 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsClientFactory.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsClientFactory.java @@ -20,6 +20,10 @@ import com.google.api.services.sheets.v4.Sheets; public interface GoogleSheetsClientFactory { - Sheets makeClient(String clientId, String clientSecret, String applicationName, String refreshToken, String accessToken); + Sheets makeClient(String clientId, + String clientSecret, + String applicationName, + String refreshToken, + String accessToken); } diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsEndpoint.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsEndpoint.java index a6d1768..f1e2e08 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsEndpoint.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsEndpoint.java @@ -35,23 +35,32 @@ import org.apache.camel.util.component.ApiMethodPropertiesHelper; /** * The google-sheets component provides access to Google Sheets. */ -@UriEndpoint(firstVersion = "2.23.0", scheme = "google-sheets", title = "Google Sheets", - syntax = "google-sheets:apiName/methodName", consumerClass = GoogleSheetsConsumer.class, consumerPrefix = "consumer", label = "api,cloud,sheets") +@UriEndpoint(firstVersion = "2.23.0", + scheme = "google-sheets", + title = "Google Sheets", + syntax = "google-sheets:apiName/methodName", + consumerClass = GoogleSheetsConsumer.class, + consumerPrefix = "consumer", + label = "api,cloud,sheets") public class GoogleSheetsEndpoint extends AbstractApiEndpoint<GoogleSheetsApiName, GoogleSheetsConfiguration> { - @UriParam - private GoogleSheetsConfiguration configuration; + @UriParam(name = "configuration") + private GoogleSheetsConfiguration endpointConfiguration; private Object apiProxy; - public GoogleSheetsEndpoint(String uri, GoogleSheetsComponent component, GoogleSheetsApiName apiName, String methodName, GoogleSheetsConfiguration endpointConfiguration) { + public GoogleSheetsEndpoint(String uri, + GoogleSheetsComponent component, + GoogleSheetsApiName apiName, + String methodName, + GoogleSheetsConfiguration endpointConfiguration) { super(uri, component, apiName, methodName, GoogleSheetsApiCollection.getCollection().getHelper(apiName), endpointConfiguration); - this.configuration = endpointConfiguration; + this.endpointConfiguration = endpointConfiguration; } @Override public Producer createProducer() throws Exception { - return new org.apache.camel.component.google.sheets.GoogleSheetsProducer(this); + return new GoogleSheetsProducer(this); } @Override @@ -79,19 +88,19 @@ public class GoogleSheetsEndpoint extends AbstractApiEndpoint<GoogleSheetsApiNam @Override protected void afterConfigureProperties() { switch (apiName) { - case SPREADSHEETS: - apiProxy = getClient().spreadsheets(); - break; - case DATA: - apiProxy = getClient().spreadsheets().values(); - break; - default: - throw new IllegalArgumentException("Invalid API name " + apiName); + case SPREADSHEETS: + apiProxy = getClient().spreadsheets(); + break; + case DATA: + apiProxy = getClient().spreadsheets().values(); + break; + default: + throw new IllegalArgumentException("Invalid API name " + apiName); } } public Sheets getClient() { - return ((GoogleSheetsComponent)getComponent()).getClient(configuration); + return ((GoogleSheetsComponent)getComponent()).getClient(endpointConfiguration); } @Override diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsProducer.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsProducer.java index abc4060..e9419fc 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsProducer.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsProducer.java @@ -39,7 +39,7 @@ public class GoogleSheetsProducer extends AbstractApiProducer<GoogleSheetsApiNam @Override protected Object doInvokeMethod(ApiMethod method, Map<String, Object> properties) throws RuntimeCamelException { - AbstractGoogleClientRequest<?> request = (AbstractGoogleClientRequest)super.doInvokeMethod(method, properties); + AbstractGoogleClientRequest<?> request = (AbstractGoogleClientRequest) super.doInvokeMethod(method, properties); try { TypeConverter typeConverter = getEndpoint().getCamelContext().getTypeConverter(); for (Entry<String, Object> p : properties.entrySet()) { diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsVerifierExtension.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsVerifierExtension.java index 26100cf..4ab0cd3 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsVerifierExtension.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/GoogleSheetsVerifierExtension.java @@ -61,9 +61,12 @@ public class GoogleSheetsVerifierExtension extends DefaultComponentVerifierExten try { GoogleSheetsConfiguration configuration = setProperties(new GoogleSheetsConfiguration(), parameters); GoogleSheetsClientFactory clientFactory = new BatchGoogleSheetsClientFactory(); - Sheets client = clientFactory.makeClient(configuration.getClientId(), configuration.getClientSecret(), configuration.getApplicationName(), - configuration.getRefreshToken(), configuration.getAccessToken()); - client.spreadsheets().get(Optional.ofNullable(parameters.get("spreadsheetId")).map(Object::toString).orElse(UUID.randomUUID().toString())).execute(); + Sheets client = clientFactory.makeClient(configuration.getClientId(), configuration.getClientSecret(), + configuration.getApplicationName(), + configuration.getRefreshToken(), configuration.getAccessToken()); + client.spreadsheets().get(Optional.ofNullable(parameters.get("spreadsheetId")) + .map(Object::toString) + .orElse(UUID.randomUUID().toString())).execute(); } catch (Exception e) { ResultErrorBuilder errorBuilder = ResultErrorBuilder.withCodeAndDescription(VerificationError.StandardCode.AUTHENTICATION, e.getMessage()) .detail("google_sheets_exception_message", e.getMessage()).detail(VerificationError.ExceptionAttribute.EXCEPTION_CLASS, e.getClass().getName()) diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/internal/GoogleSheetsConstants.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/internal/GoogleSheetsConstants.java index feb9141..0727e33 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/internal/GoogleSheetsConstants.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/internal/GoogleSheetsConstants.java @@ -29,5 +29,7 @@ public final class GoogleSheetsConstants { /** * Prevent instantiation. */ - private GoogleSheetsConstants() { } + private GoogleSheetsConstants() { + super(); + } } diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConfiguration.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConfiguration.java index 4de5222..7f949ed 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConfiguration.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConfiguration.java @@ -57,8 +57,8 @@ public class GoogleSheetsStreamConfiguration implements Cloneable { @UriParam private String spreadsheetId; - @UriParam(defaultValue = "10") - private int maxResults = 10; + @UriParam(defaultValue = "0") + private int maxResults; @UriParam private String range; diff --git a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConsumer.java b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConsumer.java index 35b8815..7142953 100644 --- a/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConsumer.java +++ b/components/camel-google-sheets/src/main/java/org/apache/camel/component/google/sheets/stream/GoogleSheetsStreamConsumer.java @@ -85,17 +85,30 @@ public class GoogleSheetsStreamConsumer extends ScheduledBatchPollingConsumer { for (ValueRange valueRange : response.getValueRanges()) { AtomicInteger rangeIndex = new AtomicInteger(1); AtomicInteger valueIndex = new AtomicInteger(); - valueRange.getValues().stream() - .limit(getConfiguration().getMaxResults()) - .map(values -> getEndpoint().createExchange(rangeIndex.get(), valueIndex.incrementAndGet(), valueRange.getRange(), valueRange.getMajorDimension(), values)) - .forEach(answer::add); + if (getConfiguration().getMaxResults() > 0) { + valueRange.getValues().stream() + .limit(getConfiguration().getMaxResults()) + .map(values -> getEndpoint().createExchange(rangeIndex.get(), valueIndex.incrementAndGet(), valueRange.getRange(), valueRange.getMajorDimension(), values)) + .forEach(answer::add); + } else { + valueRange.getValues().stream() + .map(values -> getEndpoint().createExchange(rangeIndex.get(), valueIndex.incrementAndGet(), valueRange.getRange(), valueRange.getMajorDimension(), values)) + .forEach(answer::add); + } rangeIndex.incrementAndGet(); } } else { AtomicInteger rangeIndex = new AtomicInteger(); response.getValueRanges() .stream() - .limit(getConfiguration().getMaxResults()) + .peek(valueRange -> { + if (getConfiguration().getMaxResults() > 0) { + valueRange.setValues(valueRange.getValues() + .stream() + .limit(getConfiguration().getMaxResults()) + .collect(Collectors.toList())); + } + }) .map(valueRange -> getEndpoint().createExchange(rangeIndex.incrementAndGet(), valueRange)) .forEach(answer::add); } diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/AbstractGoogleSheetsTestSupport.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/AbstractGoogleSheetsTestSupport.java index d52905c..f1e5eb7 100644 --- a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/AbstractGoogleSheetsTestSupport.java +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/AbstractGoogleSheetsTestSupport.java @@ -24,6 +24,9 @@ import java.util.Map; import java.util.Properties; import java.util.Random; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.Sheet; import com.google.api.services.sheets.v4.model.SheetProperties; import com.google.api.services.sheets.v4.model.Spreadsheet; @@ -31,8 +34,12 @@ import com.google.api.services.sheets.v4.model.SpreadsheetProperties; import com.google.api.services.sheets.v4.model.ValueRange; import org.apache.camel.CamelContext; import org.apache.camel.CamelExecutionException; +import org.apache.camel.component.google.sheets.internal.GoogleSheetsConstants; +import org.apache.camel.component.google.sheets.server.GoogleSheetsApiTestServer; +import org.apache.camel.component.google.sheets.server.GoogleSheetsApiTestServerRule; import org.apache.camel.test.junit4.CamelTestSupport; import org.apache.camel.util.IntrospectionSupport; +import org.junit.ClassRule; /** * Abstract base class for GoogleSheets Integration tests generated by Camel @@ -45,13 +52,16 @@ public class AbstractGoogleSheetsTestSupport extends CamelTestSupport { private Spreadsheet spreadsheet; + @ClassRule + public static GoogleSheetsApiTestServerRule googleSheetsApiTestServerRule = new GoogleSheetsApiTestServerRule(TEST_OPTIONS_PROPERTIES); + /** * Create test spreadsheet that is used throughout all tests. */ private void createTestSpreadsheet() { Spreadsheet spreadsheet = new Spreadsheet(); SpreadsheetProperties spreadsheetProperties = new SpreadsheetProperties(); - spreadsheetProperties.setTitle("camel-sheets-" + Math.abs(new Random().nextInt())); + spreadsheetProperties.setTitle("camel-sheets-" + new Random().nextInt(Integer.MAX_VALUE)); spreadsheet.setProperties(spreadsheetProperties); @@ -78,12 +88,12 @@ public class AbstractGoogleSheetsTestSupport extends CamelTestSupport { final Map<String, Object> headers = new HashMap<>(); // parameter type is String - headers.put("CamelGoogleSheets.spreadsheetId", spreadsheet.getSpreadsheetId()); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId", spreadsheet.getSpreadsheetId()); // parameter type is String - headers.put("CamelGoogleSheets.range", TEST_SHEET + "!A1:B2"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "range", TEST_SHEET + "!A1:B2"); // parameter type is String - headers.put("CamelGoogleSheets.valueInputOption", "USER_ENTERED"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "valueInputOption", "USER_ENTERED"); requestBodyAndHeaders("google-sheets://data/update?inBody=values", valueRange, headers); } @@ -96,8 +106,20 @@ public class AbstractGoogleSheetsTestSupport extends CamelTestSupport { final GoogleSheetsConfiguration configuration = new GoogleSheetsConfiguration(); IntrospectionSupport.setProperties(configuration, getTestOptions()); - // add GoogleSheetsComponent to Camel context + // add GoogleSheetsComponent to Camel context and use localhost url final GoogleSheetsComponent component = new GoogleSheetsComponent(context); + component.setClientFactory(new BatchGoogleSheetsClientFactory( + new NetHttpTransport.Builder() + .trustCertificatesFromJavaKeyStore( + getClass().getResourceAsStream("/" + GoogleSheetsApiTestServerRule.SERVER_KEYSTORE), + GoogleSheetsApiTestServerRule.SERVER_KEYSTORE_PASSWORD) + .build(), + new JacksonFactory()) { + @Override + protected void configure(Sheets.Builder clientBuilder) { + clientBuilder.setRootUrl(String.format("https://localhost:%s/", googleSheetsApiTestServerRule.getServerPort())); + } + }); component.setConfiguration(configuration); context.addComponent("google-sheets", component); @@ -148,17 +170,16 @@ public class AbstractGoogleSheetsTestSupport extends CamelTestSupport { return spreadsheet; } - public Spreadsheet getSpreadsheetWithTestData() { - if (spreadsheet == null) { - createTestSpreadsheet(); - } - + public Spreadsheet applyTestData(Spreadsheet spreadsheet) { createTestData(); - return spreadsheet; } public void setSpreadsheet(Spreadsheet sheet) { this.spreadsheet = sheet; } + + public GoogleSheetsApiTestServer getGoogleApiTestServer() { + return googleSheetsApiTestServerRule.getGoogleApiTestServer(); + } } diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsIntegrationTest.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsIntegrationTest.java index 800d023..b42fbaa 100644 --- a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsIntegrationTest.java +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsIntegrationTest.java @@ -27,14 +27,16 @@ import com.google.api.services.sheets.v4.model.Request; import com.google.api.services.sheets.v4.model.Spreadsheet; import com.google.api.services.sheets.v4.model.SpreadsheetProperties; import com.google.api.services.sheets.v4.model.UpdateSpreadsheetPropertiesRequest; - import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.google.sheets.internal.GoogleSheetsApiCollection; +import org.apache.camel.component.google.sheets.internal.GoogleSheetsConstants; import org.apache.camel.component.google.sheets.internal.SheetsSpreadsheetsApiMethod; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.google.sheets.server.GoogleSheetsApiTestServerAssert.assertThatGoogleApi; + /** * Test class for {@link com.google.api.services.sheets.v4.Sheets.Spreadsheets} APIs. */ @@ -45,14 +47,18 @@ public class SheetsSpreadsheetsIntegrationTest extends AbstractGoogleSheetsTestS @Test public void testCreate() throws Exception { - String title = "camel-sheets-" + Math.abs(new Random().nextInt()); + String title = "camel-sheets-" + new Random().nextInt(Integer.MAX_VALUE); Spreadsheet sheetToCreate = new Spreadsheet(); SpreadsheetProperties sheetProperties = new SpreadsheetProperties(); sheetProperties.setTitle(title); sheetToCreate.setProperties(sheetProperties); - // using com.google.api.services.sheets.v4.model.Spreadsheet message body for single parameter "content" + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasTitle(title) + .andReturnRandomSpreadsheet(); + final Spreadsheet result = requestBody("direct://CREATE", sheetToCreate); assertNotNull("create result is null", result); @@ -63,8 +69,17 @@ public class SheetsSpreadsheetsIntegrationTest extends AbstractGoogleSheetsTestS @Test public void testGet() throws Exception { + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnRandomSpreadsheet(); + Spreadsheet testSheet = getSpreadsheet(); + assertThatGoogleApi(getGoogleApiTestServer()) + .getSpreadsheetRequest(testSheet.getSpreadsheetId()) + .andReturnSpreadsheet(testSheet); + // using String message body for single parameter "spreadsheetId" final Spreadsheet result = requestBody("direct://GET", testSheet.getSpreadsheetId()); @@ -76,14 +91,24 @@ public class SheetsSpreadsheetsIntegrationTest extends AbstractGoogleSheetsTestS @Test public void testBatchUpdate() throws Exception { + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnRandomSpreadsheet(); + Spreadsheet testSheet = getSpreadsheet(); String updateTitle = "updated-" + testSheet.getProperties().getTitle(); + assertThatGoogleApi(getGoogleApiTestServer()) + .batchUpdateSpreadsheetRequest(testSheet.getSpreadsheetId()) + .updateTitle(updateTitle) + .andReturnUpdated(); + final Map<String, Object> headers = new HashMap<>(); // parameter type is String - headers.put("CamelGoogleSheets.spreadsheetId", testSheet.getSpreadsheetId()); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId", testSheet.getSpreadsheetId()); // parameter type is com.google.api.services.sheets.v4.model.BatchUpdateSpreadsheetRequest - headers.put("CamelGoogleSheets.batchUpdateSpreadsheetRequest", new BatchUpdateSpreadsheetRequest() + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "batchUpdateSpreadsheetRequest", new BatchUpdateSpreadsheetRequest() .setIncludeSpreadsheetInResponse(true) .setRequests(Collections.singletonList(new Request().setUpdateSpreadsheetProperties(new UpdateSpreadsheetPropertiesRequest() .setProperties(new SpreadsheetProperties().setTitle(updateTitle)) @@ -91,7 +116,7 @@ public class SheetsSpreadsheetsIntegrationTest extends AbstractGoogleSheetsTestS final BatchUpdateSpreadsheetResponse result = requestBodyAndHeaders("direct://BATCHUPDATE", null, headers); - assertNotNull("batchUpdate result in null", result); + assertNotNull("batchUpdate result is null", result); assertEquals(updateTitle, result.getUpdatedSpreadsheet().getProperties().getTitle()); LOG.debug("batchUpdate: " + result); diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsValuesIntegrationTest.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsValuesIntegrationTest.java index 0075f7a..405ae7a 100644 --- a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsValuesIntegrationTest.java +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/SheetsSpreadsheetsValuesIntegrationTest.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import com.google.api.services.sheets.v4.model.AppendValuesResponse; import com.google.api.services.sheets.v4.model.ClearValuesRequest; @@ -28,14 +29,17 @@ import com.google.api.services.sheets.v4.model.ClearValuesResponse; import com.google.api.services.sheets.v4.model.Spreadsheet; import com.google.api.services.sheets.v4.model.UpdateValuesResponse; import com.google.api.services.sheets.v4.model.ValueRange; - import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.google.sheets.internal.GoogleSheetsApiCollection; +import org.apache.camel.component.google.sheets.internal.GoogleSheetsConstants; import org.apache.camel.component.google.sheets.internal.SheetsSpreadsheetsValuesApiMethod; +import org.apache.camel.util.ObjectHelper; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.google.sheets.server.GoogleSheetsApiTestServerAssert.assertThatGoogleApi; + /** * Test class for {@link com.google.api.services.sheets.v4.Sheets.Spreadsheets.Values} APIs. */ @@ -46,44 +50,63 @@ public class SheetsSpreadsheetsValuesIntegrationTest extends AbstractGoogleSheet @Test public void testGet() throws Exception { + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnRandomSpreadsheet(); + Spreadsheet testSheet = getSpreadsheet(); + assertThatGoogleApi(getGoogleApiTestServer()) + .getValuesRequest(testSheet.getSpreadsheetId(), TEST_SHEET + "!A1:B2") + .andReturnValues(Collections.emptyList()); + final Map<String, Object> headers = new HashMap<>(); // parameter type is String - headers.put("CamelGoogleSheets.spreadsheetId", testSheet.getSpreadsheetId()); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId", testSheet.getSpreadsheetId()); // parameter type is String - headers.put("CamelGoogleSheets.range", TEST_SHEET + "!A1:B2"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "range", TEST_SHEET + "!A1:B2"); final ValueRange result = requestBodyAndHeaders("direct://GET", null, headers); assertNotNull("get result is null", result); assertEquals(TEST_SHEET + "!A1:B2", result.getRange()); - assertNull("expected empty value range but found entries", result.getValues()); + assertTrue("expected empty value range but found entries", ObjectHelper.isEmpty(result.getValues())); LOG.debug("get: " + result); } @Test public void testUpdate() throws Exception { + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnRandomSpreadsheet(); + Spreadsheet testSheet = getSpreadsheet(); List<List<Object>> data = Arrays.asList( Arrays.asList("A1", "B1"), Arrays.asList("A2", "B2") ); + + assertThatGoogleApi(getGoogleApiTestServer()) + .updateValuesRequest(testSheet.getSpreadsheetId(), TEST_SHEET + "!A1:B2", data) + .andReturnUpdateResponse(); + ValueRange values = new ValueRange(); values.setValues(data); final Map<String, Object> headers = new HashMap<>(); // parameter type is String - headers.put("CamelGoogleSheets.spreadsheetId", testSheet.getSpreadsheetId()); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId", testSheet.getSpreadsheetId()); // parameter type is String - headers.put("CamelGoogleSheets.range", TEST_SHEET + "!A1:B2"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "range", TEST_SHEET + "!A1:B2"); // parameter type is com.google.api.services.sheets.v4.model.ValueRange - headers.put("CamelGoogleSheets.values", values); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "values", values); // parameter type is String - headers.put("CamelGoogleSheets.valueInputOption", "USER_ENTERED"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "valueInputOption", "USER_ENTERED"); final UpdateValuesResponse result = requestBodyAndHeaders("direct://UPDATE", null, headers); @@ -98,18 +121,29 @@ public class SheetsSpreadsheetsValuesIntegrationTest extends AbstractGoogleSheet @Test public void testAppend() throws Exception { + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnRandomSpreadsheet(); + Spreadsheet testSheet = getSpreadsheet(); + List<List<Object>> data = Collections.singletonList(Arrays.asList("A10", "B10", "C10")); + + assertThatGoogleApi(getGoogleApiTestServer()) + .appendValuesRequest(testSheet.getSpreadsheetId(), TEST_SHEET + "!A10", data) + .andReturnAppendResponse(TEST_SHEET + "!A10:C10"); + final Map<String, Object> headers = new HashMap<>(); // parameter type is String - headers.put("CamelGoogleSheets.spreadsheetId", testSheet.getSpreadsheetId()); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId", testSheet.getSpreadsheetId()); // parameter type is String - headers.put("CamelGoogleSheets.range", TEST_SHEET + "!A10"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "range", TEST_SHEET + "!A10"); // parameter type is com.google.api.services.sheets.v4.model.ValueRange - headers.put("CamelGoogleSheets.values", new ValueRange().setValues(Collections.singletonList(Arrays.asList("A10", "B10", "C10")))); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "values", new ValueRange().setValues(data)); // parameter type is String - headers.put("CamelGoogleSheets.valueInputOption", "USER_ENTERED"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "valueInputOption", "USER_ENTERED"); final AppendValuesResponse result = requestBodyAndHeaders("direct://APPEND", null, headers); @@ -124,15 +158,32 @@ public class SheetsSpreadsheetsValuesIntegrationTest extends AbstractGoogleSheet @Test public void testClear() throws Exception { - Spreadsheet testSheet = getSpreadsheetWithTestData(); + String spreadsheetId = UUID.randomUUID().toString(); + + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnSpreadsheet(spreadsheetId); + + Spreadsheet testSheet = getSpreadsheet(); + + assertThatGoogleApi(getGoogleApiTestServer()) + .updateValuesRequest(spreadsheetId, TEST_SHEET + "!A1:B2", Arrays.asList(Arrays.asList("a1", "b1"), Arrays.asList("a2", "b2"))) + .andReturnUpdateResponse(); + + applyTestData(testSheet); + + assertThatGoogleApi(getGoogleApiTestServer()) + .clearValuesRequest(testSheet.getSpreadsheetId(), TEST_SHEET + "!A1:B2") + .andReturnClearResponse(TEST_SHEET + "!A1:B2"); final Map<String, Object> headers = new HashMap<>(); // parameter type is String - headers.put("CamelGoogleSheets.spreadsheetId", testSheet.getSpreadsheetId()); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId", testSheet.getSpreadsheetId()); // parameter type is String - headers.put("CamelGoogleSheets.range", TEST_SHEET + "!A1:B2"); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "range", TEST_SHEET + "!A1:B2"); // parameter type is com.google.api.services.sheets.v4.model.ClearValuesRequest - headers.put("CamelGoogleSheets.clearValuesRequest", new ClearValuesRequest()); + headers.put(GoogleSheetsConstants.PROPERTY_PREFIX + "clearValuesRequest", new ClearValuesRequest()); final ClearValuesResponse result = requestBodyAndHeaders("direct://CLEAR", null, headers); diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServer.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServer.java new file mode 100644 index 0000000..00bb91c --- /dev/null +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServer.java @@ -0,0 +1,354 @@ +/* + * 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.camel.component.google.sheets.server; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ReadListener; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.zip.GZIPInputStream; + +import com.consol.citrus.Citrus; +import com.consol.citrus.dsl.runner.DefaultTestRunner; +import com.consol.citrus.dsl.runner.TestRunner; +import com.consol.citrus.exceptions.CitrusRuntimeException; +import com.consol.citrus.http.server.HttpServer; +import com.consol.citrus.http.server.HttpServerBuilder; +import com.consol.citrus.http.servlet.GzipHttpServletResponseWrapper; +import com.consol.citrus.http.servlet.RequestCachingServletFilter; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; +import org.springframework.security.oauth2.provider.AuthorizationRequest; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter; +import org.springframework.security.oauth2.provider.client.BaseClientDetails; +import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; +import org.springframework.web.filter.OncePerRequestFilter; + +public class GoogleSheetsApiTestServer { + + private static Citrus citrus = Citrus.newInstance(); + + private final HttpServer httpServer; + private TestRunner runner; + + /** + * Prevent direct instantiation. + */ + private GoogleSheetsApiTestServer(HttpServer httpServer) { + super(); + this.httpServer = httpServer; + } + + /** + * Initialize new test run. + */ + public void init() { + runner = new DefaultTestRunner(citrus.getApplicationContext(), citrus.createTestContext()); + } + + /** + * Stop and reset current test run if any. + */ + public void reset() { + if (runner != null) { + runner.purgeEndpoints(action -> action.endpoint(httpServer)); + runner.stop(); + } + } + + /** + * Obtains the httpServer. + * @return + */ + public HttpServer getHttpServer() { + return httpServer; + } + + public void afterPropertiesSet() throws Exception { + httpServer.afterPropertiesSet(); + } + + public TestRunner getRunner() { + return runner; + } + + /** + * Builder builds server instance from given http server builder adding more setting options in fluent + * builder pattern style. + */ + public static class Builder { + private final HttpServerBuilder serverBuilder; + + private Path keyStorePath; + private String keyStorePassword; + private int securePort = 8443; + + private String basePath = ""; + + private String clientId; + private String clientSecret; + + private String accessToken; + private String refreshToken; + + public Builder(HttpServerBuilder serverBuilder) { + this.serverBuilder = serverBuilder; + } + + public Builder securePort(int securePort) { + this.securePort = securePort; + return this; + } + + public Builder keyStorePath(Path keyStorePath) { + this.keyStorePath = keyStorePath; + return this; + } + + public Builder keyStorePassword(String keyStorePass) { + this.keyStorePassword = keyStorePass; + return this; + } + + public Builder basePath(String basePath) { + this.basePath = basePath; + return this; + } + + public Builder clientId(String clientId) { + this.clientId = clientId; + return this; + } + + public Builder clientSecret(String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + public Builder accessToken(String accessToken) { + this.accessToken = accessToken; + return this; + } + + public Builder refreshToken(String refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + public GoogleSheetsApiTestServer build() throws Exception { + SslContextFactory sslContextFactory = new SslContextFactory(true); + sslContextFactory.setKeyStorePath(keyStorePath.toAbsolutePath().toString()); + sslContextFactory.setKeyStorePassword(keyStorePassword); + + HttpConfiguration parent = new HttpConfiguration(); + parent.setSecureScheme("https"); + parent.setSecurePort(securePort); + HttpConfiguration httpConfiguration = new HttpConfiguration(parent); + httpConfiguration.setCustomizers(Collections.singletonList(new SecureRequestCustomizer())); + + ServerConnector sslConnector = new ServerConnector(new org.eclipse.jetty.server.Server(), + new SslConnectionFactory(sslContextFactory, "http/1.1"), + new HttpConnectionFactory(httpConfiguration)); + sslConnector.setPort(securePort); + + serverBuilder.connector(sslConnector); + + Map<String, Filter> filterMap = new LinkedHashMap<>(); + filterMap.put("request-caching-filter", new RequestCachingServletFilter()); + filterMap.put("gzip-filter", new GzipServletFilter()); + filterMap.put("oauth2-filter", oauth2Filter()); + + Map<String, String> filterMapings = new LinkedHashMap<>(); + filterMapings.put("oauth2-filter", "/" + Optional.ofNullable(basePath).map(path -> path + "/*").orElse("*")); + serverBuilder.filterMappings(filterMapings); + + serverBuilder.filters(filterMap); + + serverBuilder.applicationContext(citrus.getApplicationContext()); + + GoogleSheetsApiTestServer server = new GoogleSheetsApiTestServer(serverBuilder.build()); + server.afterPropertiesSet(); + return server; + } + + private Filter oauth2Filter() { + BaseClientDetails clientDetails = new BaseClientDetails(); + clientDetails.setClientId(clientId); + clientDetails.setClientSecret(clientSecret); + clientDetails.setAccessTokenValiditySeconds(3000); + clientDetails.setAutoApproveScopes(Arrays.asList("read", "write")); + clientDetails.setScope(Arrays.asList("read", "write")); + clientDetails.setAuthorities(Arrays.asList(new SimpleGrantedAuthority("client_credentials"), + new SimpleGrantedAuthority("authorization_code"), + new SimpleGrantedAuthority("password"), + new SimpleGrantedAuthority("refresh_token"))); + + OAuth2AuthenticationProcessingFilter filter = new OAuth2AuthenticationProcessingFilter(); + OAuth2AuthenticationManager oauth2AuthenticationManager = new OAuth2AuthenticationManager(); + + InMemoryClientDetailsService clientDetailsService = new InMemoryClientDetailsService(); + Map<String, ClientDetails> clientDetailsStore = new HashMap<>(); + clientDetailsStore.put(clientId, clientDetails); + clientDetailsService.setClientDetailsStore(clientDetailsStore); + oauth2AuthenticationManager.setClientDetailsService(clientDetailsService); + + InMemoryTokenStore tokenStore = new InMemoryTokenStore(); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(); + authorizationRequest.setClientId(clientDetails.getClientId()); + authorizationRequest.setAuthorities(clientDetails.getAuthorities()); + authorizationRequest.setApproved(true); + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), null); + + tokenStore.storeAccessToken(new DefaultOAuth2AccessToken(accessToken), authentication); + tokenStore.storeRefreshToken(new DefaultOAuth2RefreshToken(refreshToken), authentication); + + DefaultTokenServices tokenServices = new DefaultTokenServices(); + tokenServices.setTokenStore(tokenStore); + tokenServices.setClientDetailsService(clientDetailsService); + tokenServices.setSupportRefreshToken(true); + oauth2AuthenticationManager.setTokenServices(tokenServices); + + filter.setAuthenticationManager(oauth2AuthenticationManager); + return filter; + } + } + + private static class GzipServletFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + HttpServletRequest filteredRequest = request; + HttpServletResponse filteredResponse = response; + + String contentEncoding = request.getHeader(HttpHeaders.CONTENT_ENCODING); + if (contentEncoding != null && contentEncoding.contains("gzip")) { + filteredRequest = new GzipHttpServletRequestWrapper(request); + } + + String acceptEncoding = request.getHeader(HttpHeaders.ACCEPT_ENCODING); + if (acceptEncoding != null && acceptEncoding.contains("gzip")) { + filteredResponse = new GzipHttpServletResponseWrapper(response); + } + + filterChain.doFilter(filteredRequest, filteredResponse); + + if (filteredResponse instanceof GzipHttpServletResponseWrapper) { + ((GzipHttpServletResponseWrapper) filteredResponse).finish(); + } + } + } + + private static class GzipHttpServletRequestWrapper extends HttpServletRequestWrapper { + /** + * Constructs a request adaptor wrapping the given request. + * + * @param request + * @throws IllegalArgumentException if the request is null + */ + public GzipHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return new GzipServletInputStream(getRequest()); + } + + /** + * Gzip enabled servlet input stream. + */ + private static class GzipServletInputStream extends ServletInputStream { + private final GZIPInputStream gzipStream; + + /** + * Default constructor using wrapped input stream. + * + * @param request + * @throws IOException + */ + public GzipServletInputStream(ServletRequest request) throws IOException { + super(); + gzipStream = new GZIPInputStream(request.getInputStream()); + } + + @Override + public boolean isFinished() { + try { + return gzipStream.available() == 0; + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to check gzip intput stream availability", e); + } + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(final ReadListener readListener) { + throw new UnsupportedOperationException("Unsupported operation"); + } + + @Override + public int read() { + try { + return gzipStream.read(); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read gzip input stream", e); + } + } + + @Override + public int read(byte[] b) throws IOException { + return gzipStream.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return gzipStream.read(b, off, len); + } + } + } +} diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServerAssert.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServerAssert.java new file mode 100644 index 0000000..a89edae --- /dev/null +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServerAssert.java @@ -0,0 +1,405 @@ +/* + * 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.camel.component.google.sheets.server; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import com.consol.citrus.message.MessageType; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.google.api.services.sheets.v4.model.Spreadsheet; +import com.google.api.services.sheets.v4.model.ValueRange; +import org.apache.camel.util.ObjectHelper; +import org.assertj.core.api.AbstractAssert; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class GoogleSheetsApiTestServerAssert extends AbstractAssert<GoogleSheetsApiTestServerAssert, GoogleSheetsApiTestServer> { + + private ObjectMapper mapper = new ObjectMapper() + .setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, JsonInclude.Include.NON_EMPTY)) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING) + .enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) + .disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); + + private GoogleSheetsApiTestServerAssert(GoogleSheetsApiTestServer server) { + super(server, GoogleSheetsApiTestServerAssert.class); + } + + /** + * A fluent entry point to the assertion class. + * @param server the target server to perform assertions to. + * @return + */ + public static GoogleSheetsApiTestServerAssert assertThatGoogleApi(GoogleSheetsApiTestServer server) { + return new GoogleSheetsApiTestServerAssert(server); + } + + public GetSpreadsheetAssert getSpreadsheetRequest(String spreadsheetId) { + return new GetSpreadsheetAssert(spreadsheetId); + } + + public void isRunning() { + isRunning(5000, TimeUnit.MILLISECONDS); + } + + public void isRunning(long timeout, TimeUnit timeUnit) { + ScheduledFuture<?> schedule = null; + try { + CompletableFuture<Boolean> runningProbe = new CompletableFuture<>(); + schedule = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { + if (actual.getHttpServer().isRunning()) { + runningProbe.complete(true); + } + }, 0, timeout / 10, timeUnit); + + runningProbe.get(timeout, timeUnit); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new IllegalStateException(e); + } finally { + Optional.ofNullable(schedule) + .ifPresent(future -> future.cancel(true)); + } + } + + public class GetSpreadsheetAssert { + GetSpreadsheetAssert(String spreadsheetId) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + } + + public void andReturnSpreadsheet(Spreadsheet spreadsheet) throws IOException { + String spreadsheetJson = spreadsheet.toPrettyString(); + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .get("/v4/spreadsheets/${spreadsheetId}")), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload(spreadsheetJson)) + ); + } + } + + public ClearValuesAssert clearValuesRequest(String spreadsheetId, String range) { + return new ClearValuesAssert(spreadsheetId, range); + } + + public class ClearValuesAssert { + ClearValuesAssert(String spreadsheetId, String range) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + actual.getRunner().createVariable("range", range); + } + + public void andReturnClearResponse(String clearedRange) throws IOException { + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .post("/v4/spreadsheets/${spreadsheetId}/values/${range}:clear")), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload("{" + + "\"spreadsheetId\": \"${spreadsheetId}\"," + + "\"clearedRange\": \"" + clearedRange + "\"" + + "}")) + ); + } + } + + public UpdateValuesAssert updateValuesRequest(String spreadsheetId, String range, List<List<Object>> data) { + return new UpdateValuesAssert(spreadsheetId, range, data); + } + + public class UpdateValuesAssert { + private final List<List<Object>> data; + + UpdateValuesAssert(String spreadsheetId, String range, List<List<Object>> data) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + actual.getRunner().createVariable("range", range); + this.data = data; + } + + public void andReturnUpdateResponse() throws IOException { + String valuesJson = mapper.writer().writeValueAsString(data); + + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .put("/v4/spreadsheets/${spreadsheetId}/values/${range}") + .validate("$.values.toString()", valuesJson)), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload("{" + + "\"spreadsheetId\": \"${spreadsheetId}\"," + + "\"updatedRange\": \"${range}\"," + + "\"updatedRows\": " + data.size() + "," + + "\"updatedColumns\": " + Optional.ofNullable(data.get(0)).map(Collection::size).orElse(0) + "," + + "\"updatedCells\": " + data.size() * Optional.ofNullable(data.get(0)).map(Collection::size).orElse(0) + + "}")) + ); + } + } + + public AppendValuesAssert appendValuesRequest(String spreadsheetId, String range, List<List<Object>> data) { + return new AppendValuesAssert(spreadsheetId, range, data); + } + + public class AppendValuesAssert { + private final List<List<Object>> data; + + AppendValuesAssert(String spreadsheetId, String range, List<List<Object>> data) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + actual.getRunner().createVariable("range", range); + this.data = data; + } + + public void andReturnAppendResponse(String updatedRange) throws IOException { + String valuesJson = mapper.writer().writeValueAsString(data); + + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .post("/v4/spreadsheets/${spreadsheetId}/values/${range}:append") + .validate("$.values.toString()", valuesJson)), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload("{" + + "\"spreadsheetId\": \"${spreadsheetId}\"," + + "\"updates\":" + + "{" + + "\"spreadsheetId\": \"${spreadsheetId}\"," + + "\"updatedRange\": \"" + updatedRange + "\"," + + "\"updatedRows\": " + data.size() + "," + + "\"updatedColumns\": " + Optional.ofNullable(data.get(0)).map(Collection::size).orElse(0) + "," + + "\"updatedCells\": " + data.size() * Optional.ofNullable(data.get(0)).map(Collection::size).orElse(0) + + "}" + + "}")) + ); + } + } + + public GetValuesAssert getValuesRequest(String spreadsheetId, String range) { + return new GetValuesAssert(spreadsheetId, range); + } + + public class GetValuesAssert { + GetValuesAssert(String spreadsheetId, String range) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + actual.getRunner().createVariable("range", range); + } + + public void andReturnValueRange(ValueRange valueRange) throws IOException { + String valueJson = valueRange.toPrettyString(); + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .get("/v4/spreadsheets/${spreadsheetId}/values/${range}")), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload(valueJson)) + ); + } + + public void andReturnValues(List<List<Object>> data) throws JsonProcessingException { + String valueRangeJson; + if (ObjectHelper.isEmpty(data)) { + valueRangeJson = "{" + + "\"range\": \"${range}\"," + + "\"majorDimension\": \"ROWS\"" + + "}"; + } else { + valueRangeJson = "{" + + "\"range\": \"${range}\"," + + "\"majorDimension\": \"ROWS\"," + + "\"values\":" + mapper.writer().writeValueAsString(data) + + "}"; + } + + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .get("/v4/spreadsheets/${spreadsheetId}/values/${range}")), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload(valueRangeJson)) + ); + } + } + + public BatchGetValuesAssert batchGetValuesRequest(String spreadsheetId, String range) { + return new BatchGetValuesAssert(spreadsheetId, range); + } + + public class BatchGetValuesAssert { + BatchGetValuesAssert(String spreadsheetId, String range) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + actual.getRunner().createVariable("range", range); + } + + public void andReturnValues(List<List<Object>> data) throws JsonProcessingException { + String valueRangeJson; + if (ObjectHelper.isEmpty(data)) { + valueRangeJson = "{\"spreadsheetId\": \"${spreadsheetId}\"," + + "\"valueRanges\": [" + + "{" + + "\"range\": \"${range}\"," + + "\"majorDimension\": \"ROWS\"" + + "}" + + "]}"; + } else { + valueRangeJson = "{\"spreadsheetId\": \"${spreadsheetId}\"," + + "\"valueRanges\": [" + + "{" + + "\"range\": \"${range}\"," + + "\"majorDimension\": \"ROWS\"," + + "\"values\":" + mapper.writer().writeValueAsString(data) + + "}" + + "]}"; + } + + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .get("/v4/spreadsheets/${spreadsheetId}/values:batchGet")), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload(valueRangeJson)) + ); + } + } + + public CreateSpreadsheetAssert createSpreadsheetRequest() { + return new CreateSpreadsheetAssert(); + } + + public class CreateSpreadsheetAssert { + private String title = "@ignore@"; + private String sheetTitle; + + public CreateSpreadsheetAssert hasTitle(String title) { + this.title = title; + return this; + } + + public CreateSpreadsheetAssert hasSheetTitle(String sheetTitle) { + this.sheetTitle = sheetTitle; + return this; + } + + public void andReturnRandomSpreadsheet() { + andReturnSpreadsheet("citrus:randomString(44)"); + } + + public void andReturnSpreadsheet(String spreadsheetId) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + actual.getRunner().createVariable("title", title); + + String spreadsheetJson; + if (ObjectHelper.isNotEmpty(sheetTitle)) { + actual.getRunner().createVariable("sheetTitle", sheetTitle); + spreadsheetJson = "{\"properties\":{\"title\":\"${title}\"},\"sheets\":[{\"properties\":{\"title\":\"${sheetTitle}\"}}]}"; + } else { + spreadsheetJson = "{\"properties\":{\"title\":\"${title}\"}}"; + } + + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .post("/v4/spreadsheets") + .name("create.request") + .messageType(MessageType.JSON) + .payload(spreadsheetJson)), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload("{\"spreadsheetId\":\"${spreadsheetId}\",\"properties\":{\"title\":\"citrus:jsonPath(citrus:message(create.request.payload()), '$.properties.title')\"}}")) + ); + } + } + + public BatchUpdateSpreadsheetAssert batchUpdateSpreadsheetRequest(String spreadsheetId) { + return new BatchUpdateSpreadsheetAssert(spreadsheetId); + } + + public class BatchUpdateSpreadsheetAssert { + private List<String> fields = new ArrayList<>(); + + BatchUpdateSpreadsheetAssert(String spreadsheetId) { + actual.getRunner().createVariable("spreadsheetId", spreadsheetId); + } + + public BatchUpdateSpreadsheetAssert updateTitle(String title) { + actual.getRunner().createVariable("title", title); + fields.add("title"); + return this; + } + + public void andReturnUpdated() { + actual.getRunner().async().actions( + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .receive() + .post("/v4/spreadsheets/${spreadsheetId}:batchUpdate") + .messageType(MessageType.JSON) + .payload("{" + + "\"includeSpreadsheetInResponse\":true," + + "\"requests\":[" + + "{" + + "\"updateSpreadsheetProperties\": {" + + "\"fields\":\"" + String.join(",", fields) + "\"," + + "\"properties\":{" + fields.stream().map(field -> String.format("\"%s\":\"${%s}\"", field, field)).collect(Collectors.joining(",")) + "}" + + "}" + + "}" + + "]}")), + actual.getRunner().http(action -> action.server(actual.getHttpServer()) + .send() + .response(HttpStatus.OK) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .payload("{\"spreadsheetId\":\"${spreadsheetId}\",\"updatedSpreadsheet\":{\"properties\":{\"title\":\"${title}\"},\"spreadsheetId\":\"${spreadsheetId}\"}}")) + ); + } + } +} diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServerRule.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServerRule.java new file mode 100644 index 0000000..7ecdc53 --- /dev/null +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/server/GoogleSheetsApiTestServerRule.java @@ -0,0 +1,116 @@ +/* + * 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.camel.component.google.sheets.server; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.consol.citrus.dsl.endpoint.CitrusEndpoints; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; +import org.springframework.util.SocketUtils; + +import static org.apache.camel.component.google.sheets.server.GoogleSheetsApiTestServerAssert.assertThatGoogleApi; + +public class GoogleSheetsApiTestServerRule implements TestRule { + + public static final String SERVER_KEYSTORE = "googleapis.jks"; + public static final String SERVER_KEYSTORE_PASSWORD = "secret"; + + private GoogleSheetsApiTestServer googleApiTestServer; + private int serverPort = SocketUtils.findAvailableTcpPort(); + + public GoogleSheetsApiTestServerRule(String optionFile) { + try { + Map<String, Object> testOptions = getTestOptions(optionFile); + + googleApiTestServer = new GoogleSheetsApiTestServer.Builder(CitrusEndpoints.http() + .server() + .port(serverPort) + .timeout(15000) + .defaultStatus(HttpStatus.REQUEST_TIMEOUT) + .autoStart(true)) + .keyStorePath(new ClassPathResource(SERVER_KEYSTORE).getFile().toPath()) + .keyStorePassword(SERVER_KEYSTORE_PASSWORD) + .securePort(serverPort) + .clientId(testOptions.get("clientId").toString()) + .clientSecret(testOptions.get("clientSecret").toString()) + .accessToken(testOptions.get("accessToken").toString()) + .refreshToken(testOptions.get("refreshToken").toString()) + .build(); + + assertThatGoogleApi(googleApiTestServer).isRunning(); + } catch (Exception e) { + throw new IllegalStateException("Error while reading server keystore file", e); + } + } + + @Override + public Statement apply(Statement base, Description description) { + return new GoogleSheetsApiTestServerStatement(base); + } + + /** + * Read component configuration from TEST_OPTIONS_PROPERTIES. + * @return Map of component options. + */ + private Map<String, Object> getTestOptions(String optionFile) throws IOException { + final Properties properties = new Properties(); + properties.load(getClass().getResourceAsStream(optionFile)); + + Map<String, Object> options = new HashMap<>(); + for (Map.Entry<Object, Object> entry : properties.entrySet()) { + options.put(entry.getKey().toString(), entry.getValue()); + } + + return options; + } + + /** + * Rule statement initializes and resets test server after each method. + */ + private class GoogleSheetsApiTestServerStatement extends Statement { + private final Statement base; + + GoogleSheetsApiTestServerStatement( Statement base ) { + this.base = base; + } + + @Override + public void evaluate() throws Throwable { + googleApiTestServer.init(); + try { + base.evaluate(); + } finally { + googleApiTestServer.reset(); + } + } + } + + public GoogleSheetsApiTestServer getGoogleApiTestServer() { + return googleApiTestServer; + } + + public int getServerPort() { + return serverPort; + } +} diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/AbstractGoogleSheetsStreamTestSupport.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/AbstractGoogleSheetsStreamTestSupport.java index 4767a0c..1ca4f5e 100644 --- a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/AbstractGoogleSheetsStreamTestSupport.java +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/AbstractGoogleSheetsStreamTestSupport.java @@ -16,8 +16,13 @@ */ package org.apache.camel.component.google.sheets.stream; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.sheets.v4.Sheets; import org.apache.camel.CamelContext; import org.apache.camel.component.google.sheets.AbstractGoogleSheetsTestSupport; +import org.apache.camel.component.google.sheets.BatchGoogleSheetsClientFactory; +import org.apache.camel.component.google.sheets.server.GoogleSheetsApiTestServerRule; import org.apache.camel.util.IntrospectionSupport; /** @@ -36,6 +41,18 @@ public class AbstractGoogleSheetsStreamTestSupport extends AbstractGoogleSheetsT // add GoogleSheetsComponent to Camel context final GoogleSheetsStreamComponent component = new GoogleSheetsStreamComponent(context); + component.setClientFactory(new BatchGoogleSheetsClientFactory( + new NetHttpTransport.Builder() + .trustCertificatesFromJavaKeyStore( + getClass().getResourceAsStream("/" + GoogleSheetsApiTestServerRule.SERVER_KEYSTORE), + GoogleSheetsApiTestServerRule.SERVER_KEYSTORE_PASSWORD) + .build(), + new JacksonFactory()) { + @Override + protected void configure(Sheets.Builder clientBuilder) { + clientBuilder.setRootUrl(String.format("https://localhost:%s/", googleSheetsApiTestServerRule.getServerPort())); + } + }); component.setConfiguration(configuration); context.addComponent("google-sheets-stream", component); diff --git a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/SheetsStreamConsumerIntegrationTest.java b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/SheetsStreamConsumerIntegrationTest.java index 2aa7c5d..ea79f17 100644 --- a/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/SheetsStreamConsumerIntegrationTest.java +++ b/components/camel-google-sheets/src/test/java/org/apache/camel/component/google/sheets/stream/SheetsStreamConsumerIntegrationTest.java @@ -16,7 +16,9 @@ */ package org.apache.camel.component.google.sheets.stream; +import java.util.Arrays; import java.util.List; +import java.util.UUID; import com.google.api.services.sheets.v4.model.Spreadsheet; import com.google.api.services.sheets.v4.model.ValueRange; @@ -26,6 +28,7 @@ import org.apache.camel.component.mock.MockEndpoint; import org.junit.Assert; import org.junit.Test; +import static org.apache.camel.component.google.sheets.server.GoogleSheetsApiTestServerAssert.assertThatGoogleApi; import static org.apache.camel.component.google.sheets.stream.GoogleSheetsStreamConstants.MAJOR_DIMENSION; import static org.apache.camel.component.google.sheets.stream.GoogleSheetsStreamConstants.RANGE; import static org.apache.camel.component.google.sheets.stream.GoogleSheetsStreamConstants.RANGE_INDEX; @@ -34,14 +37,35 @@ import static org.apache.camel.component.google.sheets.stream.GoogleSheetsStream public class SheetsStreamConsumerIntegrationTest extends AbstractGoogleSheetsStreamTestSupport { - private String range = "A1:B2"; + private String range = TEST_SHEET + "!A1:B2"; @Test public void testConsumeValueRange() throws Exception { - Spreadsheet testSheet = getSpreadsheetWithTestData(); + String spreadsheetId = UUID.randomUUID().toString(); - context().addRoutes(createGoogleStreamRouteBuilder(testSheet.getSpreadsheetId())); - context().startRoute("google-stream-test"); + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnSpreadsheet(spreadsheetId); + + Spreadsheet testSheet = getSpreadsheet(); + + List<List<Object>> data = Arrays.asList( + Arrays.asList("a1", "b1"), + Arrays.asList("a2", "b2") + ); + + assertThatGoogleApi(getGoogleApiTestServer()) + .updateValuesRequest(spreadsheetId, range, data) + .andReturnUpdateResponse(); + + applyTestData(testSheet); + + assertThatGoogleApi(getGoogleApiTestServer()) + .batchGetValuesRequest(testSheet.getSpreadsheetId(), range) + .andReturnValues(data); + + context().addRoutes(createGoogleStreamRouteBuilder(testSheet.getSpreadsheetId(), false)); MockEndpoint mock = getMockEndpoint("mock:result"); mock.expectedMinimumMessageCount(1); @@ -53,7 +77,7 @@ public class SheetsStreamConsumerIntegrationTest extends AbstractGoogleSheetsStr Assert.assertTrue(exchange.getIn().getHeaders().containsKey(RANGE_INDEX)); Assert.assertTrue(exchange.getIn().getHeaders().containsKey(MAJOR_DIMENSION)); Assert.assertEquals(testSheet.getSpreadsheetId(), exchange.getIn().getHeaders().get(SPREADSHEET_ID)); - Assert.assertEquals(TEST_SHEET + "!" + range, exchange.getIn().getHeaders().get(RANGE)); + Assert.assertEquals(range, exchange.getIn().getHeaders().get(RANGE)); Assert.assertEquals(1, exchange.getIn().getHeaders().get(RANGE_INDEX)); Assert.assertEquals("ROWS", exchange.getIn().getHeaders().get(MAJOR_DIMENSION)); @@ -66,13 +90,35 @@ public class SheetsStreamConsumerIntegrationTest extends AbstractGoogleSheetsStr } @Test - public void testConsumeRowValues() throws Exception { - Spreadsheet testSheet = getSpreadsheetWithTestData(); + public void testConsumeValueRangeSplitResults() throws Exception { + String spreadsheetId = UUID.randomUUID().toString(); + + assertThatGoogleApi(getGoogleApiTestServer()) + .createSpreadsheetRequest() + .hasSheetTitle("TestData") + .andReturnSpreadsheet(spreadsheetId); - context().addRoutes(createGoogleStreamRouteBuilder(testSheet.getSpreadsheetId())); - context().startRoute("google-stream-values-test"); + Spreadsheet testSheet = getSpreadsheet(); - MockEndpoint mock = getMockEndpoint("mock:rows"); + List<List<Object>> data = Arrays.asList( + Arrays.asList("a1", "b1"), + Arrays.asList("a2", "b2") + ); + + assertThatGoogleApi(getGoogleApiTestServer()) + .updateValuesRequest(spreadsheetId, range, data) + .andReturnUpdateResponse(); + + applyTestData(testSheet); + + assertThatGoogleApi(getGoogleApiTestServer()) + .batchGetValuesRequest(testSheet.getSpreadsheetId(), range) + .andReturnValues(data); + + context().addRoutes(createGoogleStreamRouteBuilder(testSheet.getSpreadsheetId(), true)); + context().startRoute("google-stream-test"); + + MockEndpoint mock = getMockEndpoint("mock:result"); mock.expectedMinimumMessageCount(2); assertMockEndpointsSatisfied(); @@ -83,7 +129,7 @@ public class SheetsStreamConsumerIntegrationTest extends AbstractGoogleSheetsStr Assert.assertTrue(exchange.getIn().getHeaders().containsKey(VALUE_INDEX)); Assert.assertTrue(exchange.getIn().getHeaders().containsKey(MAJOR_DIMENSION)); Assert.assertEquals(testSheet.getSpreadsheetId(), exchange.getIn().getHeaders().get(SPREADSHEET_ID)); - Assert.assertEquals(TEST_SHEET + "!" + range, exchange.getIn().getHeaders().get(RANGE)); + Assert.assertEquals(range, exchange.getIn().getHeaders().get(RANGE)); Assert.assertEquals(1, exchange.getIn().getHeaders().get(RANGE_INDEX)); Assert.assertEquals(1, exchange.getIn().getHeaders().get(VALUE_INDEX)); Assert.assertEquals("ROWS", exchange.getIn().getHeaders().get(MAJOR_DIMENSION)); @@ -109,14 +155,13 @@ public class SheetsStreamConsumerIntegrationTest extends AbstractGoogleSheetsStr Assert.assertEquals("b2", values.get(1)); } - private RouteBuilder createGoogleStreamRouteBuilder(String spreadsheetId) throws Exception { + private RouteBuilder createGoogleStreamRouteBuilder(String spreadsheetId, boolean splitResults) throws Exception { return new RouteBuilder() { @Override public void configure() { - from("google-sheets-stream://data?spreadsheetId=" + spreadsheetId + "&range=" + range + "&delay=2000&maxResults=5").routeId("google-stream-test").to("mock:result"); - - from("google-sheets-stream://data?spreadsheetId=" + spreadsheetId - + "&range=" + range + "&delay=2000&maxResults=5&splitResults=true").routeId("google-stream-values-test").to("mock:rows"); + from(String.format("google-sheets-stream://data?spreadsheetId=%s&range=%s&delay=20000&maxResults=5&splitResults=%s", spreadsheetId, range, splitResults)) + .routeId("google-stream-test") + .to("mock:result"); } }; } diff --git a/components/camel-google-sheets/src/test/resources/googleapis.jks b/components/camel-google-sheets/src/test/resources/googleapis.jks new file mode 100644 index 0000000..0d6097c Binary files /dev/null and b/components/camel-google-sheets/src/test/resources/googleapis.jks differ diff --git a/components/camel-google-sheets/src/test/resources/test-options.properties b/components/camel-google-sheets/src/test/resources/test-options.properties index b21908c..f2e4a6f 100644 --- a/components/camel-google-sheets/src/test/resources/test-options.properties +++ b/components/camel-google-sheets/src/test/resources/test-options.properties @@ -19,8 +19,8 @@ ## Login properties for Google Sheets Component ##################################### ## Application client id and secret -clientId= -clientSecret= +clientId=syndesis-client +clientSecret=syndesis applicationName=camel-google-sheets/1.0 -accessToken= -refreshToken= +accessToken=cd887efc-7c7d-4e8e-9580-f7502123badf +refreshToken=bdbbe5ec-6081-4c6c-8974-9c4abfc0fdcc diff --git a/parent/pom.xml b/parent/pom.xml index 9d001b4..bd2138c 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -128,6 +128,7 @@ <chronicle-wire-version>1.16.21</chronicle-wire-version> <chunk-templates-version>3.3.1</chunk-templates-version> <chunk-templates-bundle-version>3.3.1_1</chunk-templates-bundle-version> + <citrus.version>2.8.0</citrus.version> <cmis-version>1.1.0</cmis-version> <cometd-bayeux-version>6.1.11</cometd-bayeux-version> <cometd-java-client-version>3.1.2</cometd-java-client-version> diff --git a/platforms/spring-boot/components-starter/camel-google-sheets-starter/src/main/java/org/apache/camel/component/google/sheets/stream/springboot/GoogleSheetsStreamComponentConfiguration.java b/platforms/spring-boot/components-starter/camel-google-sheets-starter/src/main/java/org/apache/camel/component/google/sheets/stream/springboot/GoogleSheetsStreamComponentConfiguration.java index 8df3880..b02e6bb 100644 --- a/platforms/spring-boot/components-starter/camel-google-sheets-starter/src/main/java/org/apache/camel/component/google/sheets/stream/springboot/GoogleSheetsStreamComponentConfiguration.java +++ b/platforms/spring-boot/components-starter/camel-google-sheets-starter/src/main/java/org/apache/camel/component/google/sheets/stream/springboot/GoogleSheetsStreamComponentConfiguration.java @@ -127,7 +127,7 @@ public class GoogleSheetsStreamComponentConfiguration * number of rows in a returned value range data set or the number of * returned value ranges in a batch request. */ - private Integer maxResults = 10; + private Integer maxResults = 0; /** * Specifies the range of rows and columns in a sheet to get data from. */