+ For more details refer to the{' '}
+
+ documentation
+
+ .
+
+ >
+ ),
+ },
+ {
+ name: 'keyFormat.schema',
+ label: 'Key format schema',
+ type: 'json',
+ defined: inputFormat =>
+ oneOfKnown(deepGet(inputFormat, 'keyFormat.type'), KNOWN_TYPES, 'avro_ocf'),
+ info: (
+ <>
+ Define a reader schema to be used when parsing Avro records. This is useful when parsing
+ multiple versions of Avro OCF file data.
+ >
+ ),
+ },
+ {
+ name: 'keyFormat.protoBytesDecoder',
+ label: 'Kafka key proto bytes decoder',
+ type: 'json',
+ defined: inputFormat =>
+ oneOfKnown(deepGet(inputFormat, 'keyFormat.type'), KNOWN_TYPES, 'protobuf'),
+ required: true,
+ placeholder: `{ ... }`,
+ info: <>Specifies how to decode bytes to Protobuf record.>,
+ },
{
name: 'keyFormat.binaryAsString',
- label: 'Kafka key list binary as string',
+ label: 'Kafka key binary as string',
type: 'boolean',
defaultValue: false,
defined: inputFormat =>
@@ -498,7 +585,7 @@ export const KAFKA_METADATA_INPUT_FORMAT_FIELDS: Field[] = [
label: 'Kafka header format type',
type: 'string',
defined: typeIsKnown(KNOWN_TYPES, 'kafka'),
- placeholder: `(don't parse Kafka herders)`,
+ placeholder: `(don't parse Kafka headers)`,
suggestions: [undefined, 'string'],
},
{
@@ -529,5 +616,5 @@ export function inputFormatCanProduceNestedData(inputFormat: InputFormat): boole
inputFormat.valueFormat && inputFormatCanProduceNestedData(inputFormat.valueFormat),
);
}
- return oneOf(inputFormat.type, 'json', 'parquet', 'orc', 'avro_ocf', 'avro_stream');
+ return oneOf(inputFormat.type, 'json', 'parquet', 'orc', 'avro_ocf', 'avro_stream', 'protobuf');
}
diff --git a/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.scss b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.scss
new file mode 100644
index 00000000000..09b8609af4b
--- /dev/null
+++ b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.scss
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+.destination-pages-pane {
+ .download-button {
+ margin-top: 4px;
+ margin-left: 2px;
+ }
+}
diff --git a/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx
index f850097da26..8535db85a53 100644
--- a/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx
+++ b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx
@@ -35,6 +35,8 @@ import {
wait,
} from '../../../utils';
+import './destination-pages-pane.scss';
+
type ResultFormat = 'object' | 'array' | 'objectLines' | 'arrayLines' | 'csv';
const RESULT_FORMATS: ResultFormat[] = ['objectLines', 'object', 'arrayLines', 'array', 'csv'];
@@ -86,24 +88,28 @@ export const DestinationPagesPane = React.memo(function DestinationPagesPane(
);
}
- function getPageFilename(pageIndex: number) {
- return `${id}_page${pageIndex}.${desiredExtension}`;
+ function getPageFilename(pageIndex: number, numPages: number) {
+ const numPagesString = String(numPages);
+ const pageNumberString = String(pageIndex + 1).padStart(numPagesString.length, '0');
+ return `${id}_page_${pageNumberString}_of_${numPagesString}.${desiredExtension}`;
}
async function downloadAllPages() {
if (!pages) return;
+ const numPages = pages.length;
for (let i = 0; i < pages.length; i++) {
- downloadUrl(getPageUrl(i), getPageFilename(i));
+ downloadUrl(getPageUrl(i), getPageFilename(i, numPages));
await wait(100);
}
}
+ const numPages = pages.length;
return (
-
+
{`${
typeof numTotalRows === 'number' ? pluralIfNeeded(numTotalRows, 'row') : 'Results'
- } have been written to ${pluralIfNeeded(pages.length, 'page')}. `}
+ } have been written to ${pluralIfNeeded(numPages, 'page')}. `}
Format when downloading:{' '}
@@ -133,7 +139,7 @@ export const DestinationPagesPane = React.memo(function DestinationPagesPane(