Skip to content

Commit

Permalink
fix(Move Binary Data Node): Stringify objects before encoding them in…
Browse files Browse the repository at this point in the history
… MoveBinaryData (#4882)

* stringify objects before encoding them objects in MoveBinaryData

* add fileSize and fileType on MoveBinaryData converted data

* show `view` option for text files as well

* improve how JSON binary data is shown in the UI
  • Loading branch information
netroy authored Dec 11, 2022
1 parent f4481e2 commit 3b969d2
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 37 deletions.
9 changes: 1 addition & 8 deletions packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import {
NodeExecutionWithMetadata,
IPairedItemData,
deepCopy,
BinaryFileType,
fileTypeFromMimeType,
} from 'n8n-workflow';

import { Agent } from 'https';
Expand Down Expand Up @@ -836,13 +836,6 @@ export async function getBinaryDataBuffer(
return BinaryDataManager.getInstance().retrieveBinaryData(binaryData);
}

function fileTypeFromMimeType(mimeType: string): BinaryFileType | undefined {
if (mimeType.startsWith('image/')) return 'image';
if (mimeType.startsWith('video/')) return 'video';
if (mimeType.startsWith('text/') || mimeType.startsWith('application/json')) return 'text';
return;
}

/**
* Store an incoming IBinaryData & related buffer using the configured binary data manager.
*
Expand Down
6 changes: 5 additions & 1 deletion packages/editor-ui/src/components/BinaryDataDisplay.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div v-if="windowVisible" class="binary-data-window">
<div v-if="windowVisible" :class="['binary-data-window', binaryData?.fileType]">
<n8n-button
@click.stop="closeWindow"
size="small"
Expand Down Expand Up @@ -98,6 +98,10 @@ export default mixins(
overflow: hidden;
text-align: center;
&.json {
overflow: auto;
}
.binary-data-window-wrapper {
margin-top: .5em;
padding: 0 1em;
Expand Down
52 changes: 38 additions & 14 deletions packages/editor-ui/src/components/BinaryDataDisplayEmbed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
<source :src="embedSource" :type="binaryData.mimeType">
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
</video>
<vue-json-pretty
v-else-if="binaryData.fileType === 'json'"
:data="jsonData"
:deep="3"
:showLength="true"
/>
<embed v-else :src="embedSource" class="binary-data" :class="embedClass()"/>
</span>
</span>
Expand All @@ -19,38 +25,56 @@
<script lang="ts">
import mixins from 'vue-typed-mixins';
import { restApi } from '@/mixins/restApi';
import type { IBinaryData } from 'n8n-workflow';
import { IBinaryData, jsonParse } from 'n8n-workflow';
import type { PropType } from 'vue';
import VueJsonPretty from 'vue-json-pretty';
export default mixins(
restApi,
)
.extend({
name: 'BinaryDataDisplayEmbed',
props: [
'binaryData', // IBinaryData
],
components: {
VueJsonPretty,
},
props: {
binaryData: {
type: Object as PropType<IBinaryData>,
required: true,
},
},
data() {
return {
isLoading: true,
embedSource: '',
error: false,
jsonData: '',
};
},
async mounted() {
const id = this.binaryData?.id;
const isJSONData = this.binaryData.fileType === 'json';
if(!id) {
this.embedSource = 'data:' + this.binaryData.mimeType + ';base64,' + this.binaryData.data;
this.isLoading = false;
return;
if (isJSONData) {
this.jsonData = jsonParse(atob(this.binaryData.data));
} else {
this.embedSource = 'data:' + this.binaryData.mimeType + ';base64,' + this.binaryData.data;
}
} else {
try {
const binaryUrl = this.restApi().getBinaryUrl(id);
if (isJSONData) {
this.jsonData = await (await fetch(binaryUrl)).json();
} else {
this.embedSource = binaryUrl;
}
} catch (e) {
this.error = true;
}
}
try {
this.embedSource = this.restApi().getBinaryUrl(id);
this.isLoading = false;
} catch (e) {
this.isLoading = false;
this.error = true;
}
this.isLoading = false;
},
methods: {
embedClass(): string[] {
Expand Down
8 changes: 4 additions & 4 deletions packages/editor-ui/src/components/RunData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1060,15 +1060,15 @@ export default mixins(
this.updateNodesExecutionIssues();
},
isViewable (index: number, key: string): boolean {
const { fileType }: IBinaryData = this.binaryData[index][key];
return !!fileType && ['image', 'video'].includes(fileType);
const { fileType } = this.binaryData[index][key];
return !!fileType && ['image', 'video', 'text', 'json'].includes(fileType);
},
isDownloadable (index: number, key: string): boolean {
const { mimeType, fileName }: IBinaryData = this.binaryData[index][key];
const { mimeType, fileName } = this.binaryData[index][key];
return !!(mimeType && fileName);
},
async downloadBinaryData (index: number, key: string) {
const { id, data, fileName, fileExtension, mimeType }: IBinaryData = this.binaryData[index][key];
const { id, data, fileName, fileExtension, mimeType } = this.binaryData[index][key];
if(id) {
const url = this.restApi().getBinaryUrl(id);
Expand Down
24 changes: 16 additions & 8 deletions packages/nodes-base/nodes/MoveBinaryData/MoveBinaryData.node.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { get, set, unset } from 'lodash';
import prettyBytes from 'pretty-bytes';

import { BINARY_ENCODING, IExecuteFunctions } from 'n8n-core';

Expand All @@ -12,6 +13,7 @@ import {
INodeTypeDescription,
jsonParse,
NodeOperationError,
fileTypeFromMimeType,
} from 'n8n-workflow';

import iconv from 'iconv-lite';
Expand Down Expand Up @@ -415,21 +417,27 @@ export class MoveBinaryData implements INodeType {
newItem.binary = {};
}

const mimeType = (options.mimeType as string) || 'application/json';
const convertedValue: IBinaryData = {
data: '',
mimeType,
fileType: fileTypeFromMimeType(mimeType),
};

if (options.dataIsBase64 !== true) {
if (options.useRawData !== true) {
if (options.useRawData !== true || typeof value === 'object') {
value = JSON.stringify(value);
}

value = iconv
.encode(value as string, encoding, { addBOM: options.addBOM as boolean })
convertedValue.fileSize = prettyBytes(value.length);

convertedValue.data = iconv
.encode(value, encoding, { addBOM: options.addBOM as boolean })
.toString(BINARY_ENCODING);
} else {
convertedValue.data = value as unknown as string;
}

const convertedValue: IBinaryData = {
data: value as string,
mimeType: (options.mimeType as string) || 'application/json',
};

if (options.fileName) {
convertedValue.fileName = options.fileName as string;
}
Expand Down
1 change: 1 addition & 0 deletions packages/nodes-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@
"pdf-parse": "^1.1.1",
"pg": "^8.3.0",
"pg-promise": "^10.5.8",
"pretty-bytes": "^5.6.0",
"promise-ftp": "^1.3.5",
"redis": "^3.1.1",
"request": "^2.88.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/workflow/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type IAllExecuteFunctions =
| ITriggerFunctions
| IWebhookFunctions;

export type BinaryFileType = 'text' | 'image' | 'video';
export type BinaryFileType = 'text' | 'json' | 'image' | 'video';
export interface IBinaryData {
[key: string]: string | undefined;
data: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/workflow/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export * from './WorkflowErrors';
export * from './WorkflowHooks';
export * from './VersionedNodeType';
export { LoggerProxy, NodeHelpers, ObservableObject, TelemetryHelpers };
export { deepCopy, jsonParse, sleep } from './utils';
export { deepCopy, jsonParse, sleep, fileTypeFromMimeType } from './utils';
export {
isINodeProperties,
isINodePropertyOptions,
Expand Down
10 changes: 10 additions & 0 deletions packages/workflow/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { BinaryFileType } from './Interfaces';

export type Primitives = string | number | boolean | bigint | symbol | null | undefined;

/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
Expand Down Expand Up @@ -64,3 +66,11 @@ export const sleep = async (ms: number): Promise<void> =>
new Promise((resolve) => {
setTimeout(resolve, ms);
});

export function fileTypeFromMimeType(mimeType: string): BinaryFileType | undefined {
if (mimeType.startsWith('application/json')) return 'json';
if (mimeType.startsWith('image/')) return 'image';
if (mimeType.startsWith('video/')) return 'video';
if (mimeType.startsWith('text/')) return 'text';
return;
}
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3b969d2

Please sign in to comment.