mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(Google Sheets Node): Make it possible to set cell values empty on updates (#17224)
Co-authored-by: Milorad FIlipović <milorad@n8n.io> Co-authored-by: Nikhil Kuriakose <nikhil.kuriakose@n8n.io>
This commit is contained in:
@@ -607,6 +607,7 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
|
|||||||
:path="getPath(parameter.name)"
|
:path="getPath(parameter.name)"
|
||||||
:dependent-parameters-values="getDependentParametersValues(parameter)"
|
:dependent-parameters-values="getDependentParametersValues(parameter)"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
|
:allow-empty-strings="parameter.typeOptions?.resourceMapper?.allowEmptyValues"
|
||||||
input-size="small"
|
input-size="small"
|
||||||
label-size="small"
|
label-size="small"
|
||||||
@value-changed="valueChanged"
|
@value-changed="valueChanged"
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ type Props = {
|
|||||||
teleported?: boolean;
|
teleported?: boolean;
|
||||||
dependentParametersValues?: string | null;
|
dependentParametersValues?: string | null;
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
|
allowEmptyStrings?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
@@ -50,6 +51,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
teleported: true,
|
teleported: true,
|
||||||
dependentParametersValues: null,
|
dependentParametersValues: null,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
|
allowEmptyStrings: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { onDocumentVisible } = useDocumentVisibility();
|
const { onDocumentVisible } = useDocumentVisibility();
|
||||||
@@ -436,8 +438,8 @@ function fieldValueChanged(updateInfo: IUpdateInformation): void {
|
|||||||
let newValue = null;
|
let newValue = null;
|
||||||
if (
|
if (
|
||||||
updateInfo.value !== undefined &&
|
updateInfo.value !== undefined &&
|
||||||
updateInfo.value !== '' &&
|
|
||||||
updateInfo.value !== null &&
|
updateInfo.value !== null &&
|
||||||
|
(props.allowEmptyStrings || updateInfo.value !== '') &&
|
||||||
isResourceMapperValue(updateInfo.value)
|
isResourceMapperValue(updateInfo.value)
|
||||||
) {
|
) {
|
||||||
newValue = updateInfo.value;
|
newValue = updateInfo.value;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class GoogleSheets extends VersionedNodeType {
|
|||||||
name: 'googleSheets',
|
name: 'googleSheets',
|
||||||
icon: 'file:googleSheets.svg',
|
icon: 'file:googleSheets.svg',
|
||||||
group: ['input', 'output'],
|
group: ['input', 'output'],
|
||||||
defaultVersion: 4.6,
|
defaultVersion: 4.7,
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
description: 'Read, update and write data to Google Sheets',
|
description: 'Read, update and write data to Google Sheets',
|
||||||
};
|
};
|
||||||
@@ -27,6 +27,7 @@ export class GoogleSheets extends VersionedNodeType {
|
|||||||
4.4: new GoogleSheetsV2(baseDescription),
|
4.4: new GoogleSheetsV2(baseDescription),
|
||||||
4.5: new GoogleSheetsV2(baseDescription),
|
4.5: new GoogleSheetsV2(baseDescription),
|
||||||
4.6: new GoogleSheetsV2(baseDescription),
|
4.6: new GoogleSheetsV2(baseDescription),
|
||||||
|
4.7: new GoogleSheetsV2(baseDescription),
|
||||||
};
|
};
|
||||||
|
|
||||||
super(nodeVersions, baseDescription);
|
super(nodeVersions, baseDescription);
|
||||||
|
|||||||
@@ -96,3 +96,151 @@ describe('Google Sheet - Append or Update', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Google Sheet - Append or Update v4.6 vs v4.7 Behavior', () => {
|
||||||
|
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
|
||||||
|
let mockGoogleSheet: MockProxy<GoogleSheet>;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('v4.6: empty string in UI gets filtered out, field not sent to backend', async () => {
|
||||||
|
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||||
|
mockGoogleSheet = mock<GoogleSheet>();
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNode
|
||||||
|
.mockReturnValueOnce(mock<INode>({ typeVersion: 4.6 }))
|
||||||
|
.mockReturnValueOnce(mock<INode>({ typeVersion: 4.6 }));
|
||||||
|
|
||||||
|
mockExecuteFunctions.getInputData.mockReturnValueOnce([
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
pairedItem: { item: 0, input: undefined },
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||||
|
const params: { [key: string]: any } = {
|
||||||
|
'options.cellFormat': 'USER_ENTERED',
|
||||||
|
options: {},
|
||||||
|
'columns.mappingMode': 'defineBelow',
|
||||||
|
'columns.schema': [],
|
||||||
|
'columns.matchingColumns': ['id'],
|
||||||
|
'columns.value': {
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
// email field is NOT present here because user typed '' in UI
|
||||||
|
// and v4.6 frontend filtered it out (allowEmptyValues: false)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return params[parameterName];
|
||||||
|
});
|
||||||
|
|
||||||
|
mockGoogleSheet.getData.mockResolvedValueOnce([
|
||||||
|
['id', 'name', 'email'],
|
||||||
|
['1', 'Old Name', 'old@email.com'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockGoogleSheet.getColumnValues.mockResolvedValueOnce(['1']);
|
||||||
|
mockGoogleSheet.updateRows.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
mockGoogleSheet.prepareDataForUpdateOrUpsert.mockResolvedValueOnce({
|
||||||
|
updateData: [],
|
||||||
|
appendData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
// email is not included, so it keeps old value
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
mockGoogleSheet.appendEmptyRowsOrColumns.mockResolvedValueOnce([]);
|
||||||
|
mockGoogleSheet.appendSheetData.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
await execute.call(mockExecuteFunctions, mockGoogleSheet, 'Sheet1', '1234');
|
||||||
|
|
||||||
|
// v4.6: Only fields with non-empty values are sent to prepareDataForUpdateOrUpsert
|
||||||
|
expect(mockGoogleSheet.prepareDataForUpdateOrUpsert).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
inputData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
// email is NOT in the inputData, so cell keeps old value
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('v4.7: empty string in UI is preserved and sent to backend to clear cell', async () => {
|
||||||
|
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||||
|
mockGoogleSheet = mock<GoogleSheet>();
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNode
|
||||||
|
.mockReturnValueOnce(mock<INode>({ typeVersion: 4.7 }))
|
||||||
|
.mockReturnValueOnce(mock<INode>({ typeVersion: 4.7 }));
|
||||||
|
|
||||||
|
mockExecuteFunctions.getInputData.mockReturnValueOnce([
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
pairedItem: { item: 0, input: undefined },
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||||
|
const params: { [key: string]: any } = {
|
||||||
|
'options.cellFormat': 'USER_ENTERED',
|
||||||
|
options: {},
|
||||||
|
'columns.mappingMode': 'defineBelow',
|
||||||
|
'columns.schema': [],
|
||||||
|
'columns.matchingColumns': ['id'],
|
||||||
|
'columns.value': {
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
email: '', // Empty string is preserved in v4.7 (allowEmptyValues: true)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return params[parameterName];
|
||||||
|
});
|
||||||
|
|
||||||
|
mockGoogleSheet.getData.mockResolvedValueOnce([
|
||||||
|
['id', 'name', 'email'],
|
||||||
|
['1', 'Old Name', 'old@email.com'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockGoogleSheet.getColumnValues.mockResolvedValueOnce(['1']);
|
||||||
|
mockGoogleSheet.updateRows.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
mockGoogleSheet.prepareDataForUpdateOrUpsert.mockResolvedValueOnce({
|
||||||
|
updateData: [],
|
||||||
|
appendData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
email: '', // Empty string will clear the cell
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
mockGoogleSheet.appendEmptyRowsOrColumns.mockResolvedValueOnce([]);
|
||||||
|
mockGoogleSheet.appendSheetData.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
await execute.call(mockExecuteFunctions, mockGoogleSheet, 'Sheet1', '1234');
|
||||||
|
|
||||||
|
// v4.7: Empty strings are preserved and sent to prepareDataForUpdateOrUpsert
|
||||||
|
expect(mockGoogleSheet.prepareDataForUpdateOrUpsert).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
inputData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
email: '', // Empty string is preserved and will clear the cell
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -319,3 +319,150 @@ describe('Google Sheet - Update 4.6', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Google Sheet - Update v4.6 vs v4.7 Behavior', () => {
|
||||||
|
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
|
||||||
|
let mockGoogleSheet: MockProxy<GoogleSheet>;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('v4.6: empty string in UI gets filtered out, field not sent to backend', async () => {
|
||||||
|
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||||
|
mockGoogleSheet = mock<GoogleSheet>();
|
||||||
|
mockExecuteFunctions.getNode.mockReturnValueOnce(mock<INode>({ typeVersion: 4.6 }));
|
||||||
|
mockGoogleSheet.batchUpdate.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
mockExecuteFunctions.getInputData.mockReturnValueOnce([
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
pairedItem: { item: 0, input: undefined },
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||||
|
const params: { [key: string]: string | object } = {
|
||||||
|
options: {},
|
||||||
|
'options.cellFormat': 'USER_ENTERED',
|
||||||
|
'columns.matchingColumns': ['id'],
|
||||||
|
'columns.mappingMode': 'defineBelow',
|
||||||
|
'columns.value': {
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
// email field is NOT present here because user typed '' in UI
|
||||||
|
// and v4.6 frontend filtered it out (allowEmptyStrings: false)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return params[parameterName];
|
||||||
|
});
|
||||||
|
|
||||||
|
mockGoogleSheet.getData.mockResolvedValueOnce([
|
||||||
|
['id', 'name', 'email'],
|
||||||
|
['1', 'Old Name', 'old@email.com'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockGoogleSheet.getColumnValues.mockResolvedValueOnce(['1']);
|
||||||
|
|
||||||
|
mockGoogleSheet.prepareDataForUpdateOrUpsert.mockResolvedValueOnce({
|
||||||
|
updateData: [
|
||||||
|
{
|
||||||
|
range: 'Sheet1!B2',
|
||||||
|
values: [['John']],
|
||||||
|
},
|
||||||
|
// No update for email column - it keeps its old value
|
||||||
|
],
|
||||||
|
appendData: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await execute.call(mockExecuteFunctions, mockGoogleSheet, 'Sheet1');
|
||||||
|
|
||||||
|
// v4.6: Only name field is updated, email is not included in the update
|
||||||
|
expect(mockGoogleSheet.prepareDataForUpdateOrUpsert).toHaveBeenCalledWith({
|
||||||
|
inputData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
// email is NOT in the inputData, so cell keeps old value
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indexKey: 'id',
|
||||||
|
range: 'Sheet1!A:Z',
|
||||||
|
keyRowIndex: 0,
|
||||||
|
dataStartRowIndex: 1,
|
||||||
|
valueRenderMode: 'UNFORMATTED_VALUE',
|
||||||
|
columnNamesList: [['id', 'name', 'email']],
|
||||||
|
columnValuesList: ['1'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('v4.7: empty string in UI is preserved and sent to backend to clear cell', async () => {
|
||||||
|
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||||
|
mockGoogleSheet = mock<GoogleSheet>();
|
||||||
|
mockExecuteFunctions.getNode.mockReturnValueOnce(mock<INode>({ typeVersion: 4.7 }));
|
||||||
|
mockGoogleSheet.batchUpdate.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
mockExecuteFunctions.getInputData.mockReturnValueOnce([
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
pairedItem: { item: 0, input: undefined },
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||||
|
const params: { [key: string]: string | object } = {
|
||||||
|
options: {},
|
||||||
|
'options.cellFormat': 'USER_ENTERED',
|
||||||
|
'columns.matchingColumns': ['id'],
|
||||||
|
'columns.mappingMode': 'defineBelow',
|
||||||
|
'columns.value': {
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
email: '', // Empty string is preserved in v4.7 (allowEmptyStrings: true)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return params[parameterName];
|
||||||
|
});
|
||||||
|
|
||||||
|
mockGoogleSheet.getData.mockResolvedValueOnce([
|
||||||
|
['id', 'name', 'email'],
|
||||||
|
['1', 'Old Name', 'old@email.com'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
mockGoogleSheet.getColumnValues.mockResolvedValueOnce(['1']);
|
||||||
|
|
||||||
|
mockGoogleSheet.prepareDataForUpdateOrUpsert.mockResolvedValueOnce({
|
||||||
|
updateData: [
|
||||||
|
{
|
||||||
|
range: 'Sheet1!B2',
|
||||||
|
values: [['John']],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
range: 'Sheet1!C2',
|
||||||
|
values: [['']],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
appendData: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await execute.call(mockExecuteFunctions, mockGoogleSheet, 'Sheet1');
|
||||||
|
|
||||||
|
// v4.7: Both name and email fields are updated, email is cleared with empty string
|
||||||
|
expect(mockGoogleSheet.prepareDataForUpdateOrUpsert).toHaveBeenCalledWith({
|
||||||
|
inputData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'John',
|
||||||
|
email: '', // Empty string is preserved and will clear the cell
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indexKey: 'id',
|
||||||
|
range: 'Sheet1!A:Z',
|
||||||
|
keyRowIndex: 0,
|
||||||
|
dataStartRowIndex: 1,
|
||||||
|
valueRenderMode: 'UNFORMATTED_VALUE',
|
||||||
|
columnNamesList: [['id', 'name', 'email']],
|
||||||
|
columnValuesList: ['1'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -182,13 +182,48 @@ export const description: SheetProperties = [
|
|||||||
},
|
},
|
||||||
addAllFields: true,
|
addAllFields: true,
|
||||||
multiKeyMatch: false,
|
multiKeyMatch: false,
|
||||||
|
allowEmptyValues: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['sheet'],
|
resource: ['sheet'],
|
||||||
operation: ['appendOrUpdate'],
|
operation: ['appendOrUpdate'],
|
||||||
'@version': [{ _cnd: { gte: 4 } }],
|
'@version': [{ _cnd: { gte: 4.7 } }],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
...untilSheetSelected,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Columns',
|
||||||
|
name: 'columns',
|
||||||
|
type: 'resourceMapper',
|
||||||
|
noDataExpression: true,
|
||||||
|
default: {
|
||||||
|
mappingMode: 'defineBelow',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsDependsOn: ['sheetName.value'],
|
||||||
|
resourceMapper: {
|
||||||
|
resourceMapperMethod: 'getMappingColumns',
|
||||||
|
mode: 'upsert',
|
||||||
|
fieldWords: {
|
||||||
|
singular: 'column',
|
||||||
|
plural: 'columns',
|
||||||
|
},
|
||||||
|
addAllFields: true,
|
||||||
|
multiKeyMatch: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: ['sheet'],
|
||||||
|
operation: ['appendOrUpdate'],
|
||||||
|
'@version': [{ _cnd: { between: { from: 4, to: 4.6 } } }],
|
||||||
},
|
},
|
||||||
hide: {
|
hide: {
|
||||||
...untilSheetSelected,
|
...untilSheetSelected,
|
||||||
|
|||||||
@@ -168,13 +168,48 @@ export const description: SheetProperties = [
|
|||||||
},
|
},
|
||||||
addAllFields: true,
|
addAllFields: true,
|
||||||
multiKeyMatch: false,
|
multiKeyMatch: false,
|
||||||
|
allowEmptyValues: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['sheet'],
|
resource: ['sheet'],
|
||||||
operation: ['update'],
|
operation: ['update'],
|
||||||
'@version': [{ _cnd: { gte: 4 } }],
|
'@version': [{ _cnd: { gte: 4.7 } }],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
...untilSheetSelected,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Columns',
|
||||||
|
name: 'columns',
|
||||||
|
type: 'resourceMapper',
|
||||||
|
noDataExpression: true,
|
||||||
|
default: {
|
||||||
|
mappingMode: 'defineBelow',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsDependsOn: ['sheetName.value'],
|
||||||
|
resourceMapper: {
|
||||||
|
resourceMapperMethod: 'getMappingColumns',
|
||||||
|
mode: 'update',
|
||||||
|
fieldWords: {
|
||||||
|
singular: 'column',
|
||||||
|
plural: 'columns',
|
||||||
|
},
|
||||||
|
addAllFields: true,
|
||||||
|
multiKeyMatch: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: ['sheet'],
|
||||||
|
operation: ['update'],
|
||||||
|
'@version': [{ _cnd: { between: { from: 4, to: 4.6 } } }],
|
||||||
},
|
},
|
||||||
hide: {
|
hide: {
|
||||||
...untilSheetSelected,
|
...untilSheetSelected,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const versionDescription: INodeTypeDescription = {
|
|||||||
name: 'googleSheets',
|
name: 'googleSheets',
|
||||||
icon: 'file:googleSheets.svg',
|
icon: 'file:googleSheets.svg',
|
||||||
group: ['input', 'output'],
|
group: ['input', 'output'],
|
||||||
version: [3, 4, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6],
|
version: [3, 4, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7],
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
description: 'Read, update and write data to Google Sheets',
|
description: 'Read, update and write data to Google Sheets',
|
||||||
defaults: {
|
defaults: {
|
||||||
|
|||||||
@@ -1391,6 +1391,7 @@ export interface ResourceMapperTypeOptionsBase {
|
|||||||
hint?: string;
|
hint?: string;
|
||||||
};
|
};
|
||||||
showTypeConversionOptions?: boolean;
|
showTypeConversionOptions?: boolean;
|
||||||
|
allowEmptyValues?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce at least one of resourceMapperMethod or localResourceMapperMethod
|
// Enforce at least one of resourceMapperMethod or localResourceMapperMethod
|
||||||
|
|||||||
Reference in New Issue
Block a user