diff --git a/kamelets/google-sheets-sink.kamelet.yaml b/kamelets/google-sheets-sink.kamelet.yaml
new file mode 100644
index 000000000..63488bd1c
--- /dev/null
+++ b/kamelets/google-sheets-sink.kamelet.yaml
@@ -0,0 +1,187 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+ name: google-sheets-sink
+ annotations:
+ camel.apache.org/kamelet.support.level: "Stable"
+ camel.apache.org/catalog.version: "4.1.0-SNAPSHOT"
+ camel.apache.org/kamelet.icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA2NCA4OCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjQgODg7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojMERDNTRDO30KCS5zdDF7ZmlsbDojMDg5NjJEO30KCS5zdDJ7ZmlsbDojRkRGRkZGO30KPC9zdHlsZT4KPGcgaWQ9IkxheWVyXzQiPgoJPHBhdGggY2xhc3M9InN0MCIgZD0iTTU4LDg4SDZjLTMuMywwLTYtMi43LTYtNlY2YzAtMy4zLDIuNy02LDYtNmgzNmwyMiwyMnY2MEM2NCw4NS4zLDYxLjMsODgsNTgsODh6Ii8+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNNDIsMGwyMiwyMkg0MlYweiIvPgoJPHBhdGggY2xhc3M9InN0MiIgZD0iTTEyLDM0LjV2MjhoNDB2LTI4SDEyeiBNMTcsMzkuNWgxMi41VjQ2SDE3VjM5LjV6IE0xNyw1MWgxMi41djYuNUgxN1Y1MXogTTQ3LDU3LjVIMzQuNVY1MUg0N1Y1Ny41eiBNNDcsNDYKCQlIMzQuNXYtNi41SDQ3VjQ2eiIvPgo8L2c+Cjwvc3ZnPgo="
+ camel.apache.org/provider: "Apache Software Foundation"
+ camel.apache.org/kamelet.group: "Google Sheets"
+ camel.apache.org/kamelet.namespace: "GCP"
+ labels:
+ camel.apache.org/kamelet.type: "sink"
+spec:
+ definition:
+ title: "Google Sheets Sink"
+ description: |-
+ Send data to Google Sheets and update/append values on a spreadsheet.
+ required:
+ - spreadsheetId
+ - clientId
+ - accessToken
+ - refreshToken
+ - clientSecret
+ type: object
+ properties:
+ spreadsheetId:
+ title: Spreadsheet ID
+ description: The Spreadsheet ID to be used as identifier
+ type: string
+ clientId:
+ title: Client Id
+ description: Client ID of the sheets application
+ type: string
+ format: password
+ x-descriptors:
+ - urn:alm:descriptor:com.tectonic.ui:password
+ - urn:camel:group:credentials
+ clientSecret:
+ title: Client Secret
+ description: Client Secret of the sheets application
+ type: string
+ format: password
+ x-descriptors:
+ - urn:alm:descriptor:com.tectonic.ui:password
+ - urn:camel:group:credentials
+ accessToken:
+ title: Access Token
+ description: OAuth 2 access token for google sheets application. This typically expires after an hour so refreshToken is recommended for long term usage.
+ type: string
+ format: password
+ x-descriptors:
+ - urn:alm:descriptor:com.tectonic.ui:password
+ - urn:camel:group:credentials
+ refreshToken:
+ title: Refresh Token
+ description: OAuth 2 refresh token for google sheets application. Using this, the Google Calendar component can obtain a new accessToken whenever the current one expires - a necessity if the application is long-lived.
+ type: string
+ format: password
+ x-descriptors:
+ - urn:alm:descriptor:com.tectonic.ui:password
+ - urn:camel:group:credentials
+ applicationName:
+ title: Application name
+ description: Google Sheets application name
+ type: string
+ operation:
+ title: Operation mode
+ description: Operation to execute (update or append)
+ type: string
+ enum: [ "update", "append" ]
+ default: append
+ example: append
+ range:
+ title: Cells range to write data to
+ description: the range of rows and columns in a sheet to get data from.
+ type: string
+ example: "A1:B3"
+ majorDimension:
+ title: Major Dimension
+ description: Specifies the major dimension that the given values should use (ROWS or COLUMNS).
+ type: string
+ enum: [ "COLUMNS", "ROWS" ]
+ default: "ROWS"
+ example: "ROWS"
+ columnNames:
+ title: Column Names
+ description: Optional custom column names that map to cell coordinates based on their position.
+ type: string
+ default: "A"
+ valueInputOption:
+ title: Value Input Option
+ description: Controls how the entered values should be be interpreted when adding them.
+ type: string
+ enum: [ "USER_ENTERED", "RAW" ]
+ default: "USER_ENTERED"
+ example: "USER_ENTERED"
+ dataTypes:
+ in:
+ default: json-struct
+ types:
+ json-struct:
+ format: "google-sheets:application-x-struct"
+ description: |-
+ Special Json representation of Google Sheets ValueRange object with just row and column values as a generic JsonNode.
+ Each cell value is represented by a Json property named after the respective row (A-Z) or column (1-n) depending on the given majorDimension.
+ Custom column names are supported in order to use custom property names instead of generic row (A-Z) or column (1-n) coordinates.
+ The given Json struct is ready to be transformed into a proper Google Sheets ValueRange object that can be used in the update/append values operation.
+ The data type uses a set of header entries to determine properties such as spreadsheetId, the target cell range, the majorDimension and so on.
+ headers:
+ CamelGoogleSheets.range:
+ title: Range
+ description: Cells range to write data to.
+ default: A:A
+ type: string
+ CamelGoogleSheets.spreadsheetId:
+ title: Spreadsheet id
+ description: The Spreadsheet ID to be used as identifier.
+ type: string
+ CamelGoogleSheets.majorDimension:
+ title: Major dimension
+ description: Specifies the major dimension that the given values should use (ROWS or COLUMNS).
+ default: ROWS
+ type: string
+ CamelGoogleSheets.columnNames:
+ title: Column Names
+ description: Optional custom column names that map to cell coordinates based on their position.
+ default: A
+ type: string
+ CamelGoogleSheets.valueInputOption:
+ title: Value Input Option
+ description: Controls how the entered values should be be interpreted when adding them.
+ default: USER_ENTERED
+ type: string
+ mediaType: application/json
+ dependencies:
+ - "mvn:org.apache.camel.kamelets:camel-kamelets-utils:4.1.0-SNAPSHOT"
+ - "camel:jackson"
+ - "camel:kamelet"
+ - "camel:google-sheets"
+ template:
+ from:
+ uri: "kamelet:source"
+ steps:
+ - set-header:
+ name: CamelGoogleSheets.spreadsheetId
+ simple: "{{spreadsheetId}}"
+ - set-header:
+ name: CamelGoogleSheets.range
+ simple: "{{?range}}"
+ - set-header:
+ name: CamelGoogleSheets.majorDimension
+ simple: "{{?majorDimension}}"
+ - set-header:
+ name: CamelGoogleSheets.columnNames
+ simple: "{{?columnNames}}"
+ - set-header:
+ name: CamelGoogleSheets.valueInputOption
+ simple: "{{?valueInputOption}}"
+ - transform:
+ to-type: "google-sheets:application-x-struct"
+ - to:
+ uri: "google-sheets:data/{{operation}}"
+ parameters:
+ spreadsheetId: "{{spreadsheetId}}"
+ clientId: "{{clientId}}"
+ accessToken: "{{accessToken}}"
+ refreshToken: "{{refreshToken}}"
+ clientSecret: "{{clientSecret}}"
+ applicationName: "{{?applicationName}}"
+ range: "{{?range}}"
diff --git a/kamelets/google-sheets-source.kamelet.yaml b/kamelets/google-sheets-source.kamelet.yaml
index 210333f79..c3553799c 100644
--- a/kamelets/google-sheets-source.kamelet.yaml
+++ b/kamelets/google-sheets-source.kamelet.yaml
@@ -91,7 +91,7 @@ spec:
type: string
splitResults:
title: Split Results
- description: True if value range result should be split into rows or columns to process each of them individually.
+ description: True if value range result should be split into rows or columns to process each of them individually.
type: boolean
x-descriptors:
- 'urn:alm:descriptor:com.tectonic.ui:checkbox'
@@ -101,9 +101,62 @@ spec:
description: the range of rows and columns in a sheet to get data from.
type: string
example: "A1:B3"
- types:
+ majorDimension:
+ title: Major Dimension
+ description: Specifies the major dimension that the given values should use (ROWS or COLUMNS).
+ type: string
+ enum: [ "COLUMNS", "ROWS" ]
+ default: "ROWS"
+ example: "ROWS"
+ columnNames:
+ title: Column Names
+ description: Optional custom column names that map to cell coordinates based on their position.
+ type: string
+ default: "A"
+ dataTypes:
+ default:
out:
- mediaType: application/json
+ default: json
+ headers:
+ CamelGoogleSheets.range:
+ title: Range
+ description: Cells range to write data to.
+ default: A:A
+ type: string
+ CamelGoogleSheets.spreadsheetId:
+ title: Spreadsheet id
+ description: The Spreadsheet ID to be used as identifier.
+ type: string
+ CamelGoogleSheets.majorDimension:
+ title: Major dimension
+ description: Specifies the major dimension that the given values should use (ROWS or COLUMNS).
+ default: ROWS
+ type: string
+ CamelGoogleSheets.columnNames:
+ title: Column Names
+ description: Optional custom column names that map to cell coordinates based on their position.
+ default: A
+ type: string
+ CamelGoogleSheets.splitResults:
+ title: Split Results
+ description: True if value range result should be split into rows or columns to process each of them individually.
+ default: true
+ type: boolean
+ types:
+ json:
+ format: "application-json"
+ description: |-
+ Json representation of a GoogleSheets ValueRange object that holds all values for the given cell range.
+ Or Json array of values for a single row/column in the range when 'splitResults' mode is enabled.
+ mediaType: application/json
+ json-struct:
+ format: "google-sheets:application-x-struct"
+ description: |-
+ Special Json representation of Google Sheets ValueRange object with just row and column values as a generic JsonNode.
+ Each cell value is represented by a Json property named after the respective row (A-Z) or column (1-n) depending on the given majorDimension.
+ Custom column names are supported in order to use custom property names instead of generic row (A-Z) or column (1-n) coordinates.
+ The produced Json struct is ready to be transformed back into a proper Google Sheets ValueRange object that can be used in an update/append values operation.
+ mediaType: application/json
dependencies:
- "camel:jackson"
- "camel:kamelet"
@@ -124,4 +177,19 @@ spec:
steps:
- marshal:
json: {}
+ - set-header:
+ name: CamelGoogleSheets.spreadsheetId
+ simple: "{{spreadsheetId}}"
+ - set-header:
+ name: CamelGoogleSheets.range
+ simple: "{{?range}}"
+ - set-header:
+ name: CamelGoogleSheets.majorDimension
+ simple: "{{?majorDimension}}"
+ - set-header:
+ name: CamelGoogleSheets.columnNames
+ simple: "{{?columnNames}}"
+ - set-header:
+ name: CamelGoogleSheets.splitResults
+ simple: "{{?splitResults}}"
- to: "kamelet:sink"
diff --git a/library/camel-kamelets-utils/pom.xml b/library/camel-kamelets-utils/pom.xml
index b4f56daa7..b5886188f 100644
--- a/library/camel-kamelets-utils/pom.xml
+++ b/library/camel-kamelets-utils/pom.xml
@@ -97,6 +97,11 @@
camel-google-storageprovided
+
+ org.apache.camel
+ camel-google-sheets
+ provided
+
@@ -112,6 +117,12 @@
${junit-jupiter-version}test
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit-jupiter-version}
+ test
+ org.junit.jupiter
@@ -120,6 +131,13 @@
test
+
+ org.skyscreamer
+ jsonassert
+ ${jsonassert-version}
+ test
+
+
org.apache.logging.log4j
diff --git a/library/camel-kamelets-utils/src/main/java/org/apache/camel/kamelets/utils/format/converter/google/sheets/CellCoordinate.java b/library/camel-kamelets-utils/src/main/java/org/apache/camel/kamelets/utils/format/converter/google/sheets/CellCoordinate.java
new file mode 100644
index 000000000..60553b299
--- /dev/null
+++ b/library/camel-kamelets-utils/src/main/java/org/apache/camel/kamelets/utils/format/converter/google/sheets/CellCoordinate.java
@@ -0,0 +1,195 @@
+/*
+ * 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.kamelets.utils.format.converter.google.sheets;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.apache.camel.util.ObjectHelper;
+
+public class CellCoordinate {
+
+ private int rowIndex;
+ private int columnIndex;
+
+ /**
+ * Prevent direct instantiation
+ */
+ CellCoordinate() {
+ super();
+ }
+
+ /**
+ * Construct grid coordinate from given cell identifier representation in A1 form. For instance convert
+ * cell id string "A1" to a coordinate with rowIndex=0, and columnIndex=0.
+ *
+ * @param cellId
+ * @return
+ */
+ public static CellCoordinate fromCellId(String cellId) {
+ CellCoordinate coordinate = new CellCoordinate();
+
+ if (cellId != null) {
+ coordinate.setRowIndex(getRowIndex(cellId));
+ coordinate.setColumnIndex(getColumnIndex(cellId));
+ }
+
+ return coordinate;
+ }
+
+ /**
+ * Evaluate the column index from cellId in A1 notation. Column name letters are translated to numeric column index values.
+ * Column "A" will result in column index 0. Method does support columns with combined name letters such as "AA" where this is
+ * the first column after "Z" resulting in a column index of 26.
+ *
+ * @param cellId
+ * @return
+ */
+ protected static int getColumnIndex(String cellId) {
+ char[] characters = cellId.toCharArray();
+ List chars = IntStream.range(0, characters.length)
+ .mapToObj(i -> characters[i])
+ .filter(c -> !Character.isDigit(c))
+ .map(Character::toUpperCase)
+ .map(Character::getNumericValue)
+ .collect(Collectors.toList());
+
+ if (chars.size() > 1) {
+ int index = 0;
+ for (int i = 0; i < chars.size(); i++) {
+ if (i == chars.size() -1) {
+ index += chars.get(i) - Character.getNumericValue('A');
+ } else {
+ index += ((chars.get(i) - Character.getNumericValue('A')) + 1) * 26;
+ }
+ }
+ return index;
+ } else if (chars.size() == 1) {
+ return chars.get(0) - Character.getNumericValue('A');
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Evaluates the row index from a given cellId in A1 notation. Extracts the row number and translates that to an numeric
+ * index value beginning with 0.
+ *
+ * @param cellId
+ * @return
+ */
+ protected static int getRowIndex(String cellId) {
+ char[] characters = cellId.toCharArray();
+ String index = IntStream.range(0, characters.length)
+ .mapToObj(i -> characters[i])
+ .filter(Character::isDigit)
+ .map(String::valueOf)
+ .collect(Collectors.joining());
+
+ if (ObjectHelper.isNotEmpty(index)) {
+ return Integer.parseInt(index) - 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Evaluates column name in A1 notation based on the column index. Index 0 will be "A" and index 25 will be "Z". Method also supports
+ * name overflow where index 26 will be "AA" and index 51 will be "AZ" and so on.
+ *
+ * @param columnIndex
+ * @return
+ */
+ public static String getColumnName(int columnIndex) {
+ String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ StringBuilder columnName = new StringBuilder();
+
+ int index = columnIndex;
+ int overflowIndex = -1;
+ while (index > 25) {
+ overflowIndex++;
+ index -= 26;
+ }
+
+ if (overflowIndex >= 0) {
+ columnName.append(alphabet.toCharArray()[overflowIndex]);
+ }
+
+ columnName.append(alphabet.toCharArray()[index]);
+
+ return columnName.toString();
+ }
+
+ /**
+ * Special getter for column name where user is able to give set of user defined column names. When given column index is resolvable via custom names
+ * the custom column name is returned otherwise the evaluated default column name is returned.
+ *
+ * @param columnIndex
+ * @param columnStartIndex
+ * @param columnNames
+ * @return
+ */
+ public static String getColumnName(int columnIndex, int columnStartIndex, String ... columnNames) {
+ String columnName = getColumnName(columnIndex);
+
+ int index;
+ if (columnStartIndex > 0) {
+ index = columnIndex % columnStartIndex;
+ } else {
+ index = columnIndex;
+ }
+
+ if (index < columnNames.length) {
+ String name = columnNames[index];
+ if (columnName.equals(name)) {
+ return columnName;
+ } else {
+ return name;
+ }
+ }
+
+ return columnName;
+ }
+
+ public int getRowIndex() {
+ return rowIndex;
+ }
+
+ /**
+ * Specifies the rowIndex.
+ *
+ * @param rowIndex
+ */
+ public void setRowIndex(int rowIndex) {
+ this.rowIndex = rowIndex;
+ }
+
+ public int getColumnIndex() {
+ return columnIndex;
+ }
+
+ /**
+ * Specifies the columnIndex.
+ *
+ * @param columnIndex
+ */
+ public void setColumnIndex(int columnIndex) {
+ this.columnIndex = columnIndex;
+ }
+}
diff --git a/library/camel-kamelets-utils/src/main/java/org/apache/camel/kamelets/utils/format/converter/google/sheets/GoogleSheetsJsonStructDataType.java b/library/camel-kamelets-utils/src/main/java/org/apache/camel/kamelets/utils/format/converter/google/sheets/GoogleSheetsJsonStructDataType.java
new file mode 100644
index 000000000..4336a10c4
--- /dev/null
+++ b/library/camel-kamelets-utils/src/main/java/org/apache/camel/kamelets/utils/format/converter/google/sheets/GoogleSheetsJsonStructDataType.java
@@ -0,0 +1,337 @@
+/*
+ * 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.kamelets.utils.format.converter.google.sheets;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.api.services.sheets.v4.model.ValueRange;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.Message;
+import org.apache.camel.component.google.sheets.internal.GoogleSheetsConstants;
+import org.apache.camel.component.google.sheets.stream.GoogleSheetsStreamConstants;
+import org.apache.camel.kamelets.utils.format.converter.json.Json;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.spi.DataTypeTransformer;
+import org.apache.camel.spi.Transformer;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Data type supports generic JsonNode representation of Google Sheets row and column values.
+ * Transforms generic JsonNode struct to/from a Google Sheets ValueRange object.
+ * Supports both inbound and outbound transformation depending on the given message body content.
+ * When Google Sheets ValueRange object is given as message body (e.g. as a result of a get values operation) the transformer will transform into generic Json struct.
+ * When generic Json struct is given as a message body transformer will transform into a proper ValueRange object that is ready to be used in an update/append values operation.
+ * Implementation also supports splitResults setting where a set of values is split into its individual items.
+ */
+@DataTypeTransformer(name = "google-sheets:application-x-struct")
+public class GoogleSheetsJsonStructDataType extends Transformer {
+
+ private static final String ROW_PREFIX = "#";
+
+ @Override
+ public void transform(Message message, DataType fromType, DataType toType) {
+ final Optional valueRange = getValueRangeBody(message);
+
+ String range = message.getHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "range", "A:A").toString();
+ String majorDimension = message.getHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "majorDimension", RangeCoordinate.DIMENSION_ROWS).toString();
+ String spreadsheetId = message.getHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId", "").toString();
+ String[] columnNames = message.getHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "columnNames", "A").toString().split(",");
+
+ boolean splitResults = Boolean.parseBoolean(message.getHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "splitResults", "false").toString());
+
+ if (valueRange.isPresent()) {
+ message.setBody(transformFromValueRangeModel(message, valueRange.get(), spreadsheetId, range, majorDimension, columnNames));
+ } else if (splitResults) {
+ message.setBody(transformFromSplitValuesModel(message, spreadsheetId, range, majorDimension, columnNames));
+ } else {
+ String valueInputOption = message.getHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "valueInputOption", "USER_ENTERED").toString();
+ message.setBody(transformToValueRangeModel(message, spreadsheetId, range, majorDimension, valueInputOption, columnNames));
+ }
+ }
+
+ /**
+ * Constructs proper ValueRange object from given generic Json struct.
+ * @param message
+ * @param spreadsheetId
+ * @param range
+ * @param majorDimension
+ * @param valueInputOption
+ * @param columnNames
+ * @return
+ */
+ private ValueRange transformToValueRangeModel(Message message, String spreadsheetId, String range, String majorDimension, String valueInputOption, String[] columnNames) {
+ try {
+ List jsonBeans = bodyAsJsonBeans(message);
+
+ ValueRange valueRange = new ValueRange();
+ List> values = new ArrayList<>();
+
+ if (ObjectHelper.isNotEmpty(jsonBeans)) {
+ final ArrayList properties = createCoordinateNameSpec(range, majorDimension, columnNames);
+
+ for (String json : jsonBeans) {
+ Map dataShape = Json.MAPPER.reader().forType(Map.class).readValue(json);
+
+ if (dataShape.containsKey("spreadsheetId")) {
+ spreadsheetId = Optional.ofNullable(dataShape.remove("spreadsheetId"))
+ .map(Object::toString)
+ .orElse(spreadsheetId);
+ }
+
+ List