mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Merge 83fe8ac118
into d2dd1796a8
This commit is contained in:
commit
1ec7b9c763
|
@ -22,6 +22,7 @@ export async function supabaseApiRequest(
|
||||||
qs: IDataObject = {},
|
qs: IDataObject = {},
|
||||||
uri?: string,
|
uri?: string,
|
||||||
headers: IDataObject = {},
|
headers: IDataObject = {},
|
||||||
|
returnData: boolean = true,
|
||||||
) {
|
) {
|
||||||
const credentials = await this.getCredentials<{
|
const credentials = await this.getCredentials<{
|
||||||
host: string;
|
host: string;
|
||||||
|
@ -29,9 +30,7 @@ export async function supabaseApiRequest(
|
||||||
}>('supabaseApi');
|
}>('supabaseApi');
|
||||||
|
|
||||||
const options: IRequestOptions = {
|
const options: IRequestOptions = {
|
||||||
headers: {
|
headers: returnData ? { Prefer: 'return=representation' } : { Prefer: 'return=minimal' },
|
||||||
Prefer: 'return=representation',
|
|
||||||
},
|
|
||||||
method,
|
method,
|
||||||
qs,
|
qs,
|
||||||
body,
|
body,
|
||||||
|
|
|
@ -170,6 +170,19 @@ export const rowFields: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return Inserted Data',
|
||||||
|
name: 'returnData',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: ['row'],
|
||||||
|
operation: ['create'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'Whether to return the inserted data. Disable if RLS policies restrict SELECT.',
|
||||||
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* row:delete */
|
/* row:delete */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
|
@ -132,6 +132,7 @@ export class Supabase implements INodeType {
|
||||||
|
|
||||||
if (operation === 'create') {
|
if (operation === 'create') {
|
||||||
const records: IDataObject[] = [];
|
const records: IDataObject[] = [];
|
||||||
|
const returnDataParam = this.getNodeParameter('returnData', 0, true) as boolean;
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
const record: IDataObject = {};
|
const record: IDataObject = {};
|
||||||
|
@ -159,12 +160,17 @@ export class Supabase implements INodeType {
|
||||||
const endpoint = `/${tableId}`;
|
const endpoint = `/${tableId}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const createdRows: IDataObject[] = await supabaseApiRequest.call(
|
const response = await supabaseApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
endpoint,
|
endpoint,
|
||||||
records,
|
records,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
returnDataParam,
|
||||||
);
|
);
|
||||||
|
const createdRows = returnDataParam ? (response as IDataObject[]) : [];
|
||||||
createdRows.forEach((row, i) => {
|
createdRows.forEach((row, i) => {
|
||||||
const executionData = this.helpers.constructExecutionMetaData(
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
this.helpers.returnJsonArray(row),
|
this.helpers.returnJsonArray(row),
|
||||||
|
@ -172,6 +178,13 @@ export class Supabase implements INodeType {
|
||||||
);
|
);
|
||||||
returnData.push(...executionData);
|
returnData.push(...executionData);
|
||||||
});
|
});
|
||||||
|
if (!returnDataParam) {
|
||||||
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
|
this.helpers.returnJsonArray({ success: true, message: 'Row inserted' }),
|
||||||
|
{ itemData: mapPairedItemsFrom(records) },
|
||||||
|
);
|
||||||
|
returnData.push(...executionData);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.continueOnFail()) {
|
if (this.continueOnFail()) {
|
||||||
const executionData = this.helpers.constructExecutionMetaData(
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
|
|
|
@ -15,7 +15,8 @@ import { Supabase } from '../Supabase.node';
|
||||||
describe('Test Supabase Node', () => {
|
describe('Test Supabase Node', () => {
|
||||||
const node = new Supabase();
|
const node = new Supabase();
|
||||||
|
|
||||||
const input = [{ json: {} }];
|
// Input for create tests
|
||||||
|
const input = [{ json: { email: 'test@example.com', phone: '+1234567890' } }];
|
||||||
|
|
||||||
const createMockExecuteFunction = (
|
const createMockExecuteFunction = (
|
||||||
nodeParameters: IDataObject,
|
nodeParameters: IDataObject,
|
||||||
|
@ -29,7 +30,6 @@ describe('Test Supabase Node', () => {
|
||||||
options?: IGetNodeParameterOptions | undefined,
|
options?: IGetNodeParameterOptions | undefined,
|
||||||
) {
|
) {
|
||||||
const parameter = options?.extractValue ? `${parameterName}.value` : parameterName;
|
const parameter = options?.extractValue ? `${parameterName}.value` : parameterName;
|
||||||
|
|
||||||
const parameterValue = get(nodeParameters, parameter, fallbackValue);
|
const parameterValue = get(nodeParameters, parameter, fallbackValue);
|
||||||
|
|
||||||
if ((parameterValue as IDataObject)?.nodeOperationError) {
|
if ((parameterValue as IDataObject)?.nodeOperationError) {
|
||||||
|
@ -45,10 +45,11 @@ describe('Test Supabase Node', () => {
|
||||||
getInputData: () => input,
|
getInputData: () => input,
|
||||||
helpers: {
|
helpers: {
|
||||||
constructExecutionMetaData: (
|
constructExecutionMetaData: (
|
||||||
_inputData: INodeExecutionData[],
|
inputData: INodeExecutionData[],
|
||||||
_options: { itemData: IPairedItemData | IPairedItemData[] },
|
_options: { itemData: IPairedItemData | IPairedItemData[] },
|
||||||
) => [],
|
) => inputData,
|
||||||
returnJsonArray: (_jsonData: IDataObject | IDataObject[]) => [],
|
returnJsonArray: (jsonData: IDataObject | IDataObject[]) =>
|
||||||
|
Array.isArray(jsonData) ? jsonData.map((data) => ({ json: data })) : [{ json: jsonData }],
|
||||||
},
|
},
|
||||||
} as unknown as IExecuteFunctions;
|
} as unknown as IExecuteFunctions;
|
||||||
return fakeExecuteFunction;
|
return fakeExecuteFunction;
|
||||||
|
@ -96,4 +97,98 @@ describe('Test Supabase Node', () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Create Operation', () => {
|
||||||
|
it('should create a row and return data when returnData is true', async () => {
|
||||||
|
const supabaseApiRequest = jest
|
||||||
|
.spyOn(utils, 'supabaseApiRequest')
|
||||||
|
.mockResolvedValue([{ email: 'test@example.com', phone: '+1234567890' }]);
|
||||||
|
|
||||||
|
const fakeExecuteFunction = createMockExecuteFunction({
|
||||||
|
resource: 'row',
|
||||||
|
operation: 'create',
|
||||||
|
tableId: 'signups',
|
||||||
|
dataToSend: 'autoMapInputData',
|
||||||
|
inputsToIgnore: '',
|
||||||
|
returnData: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await node.execute.call(fakeExecuteFunction);
|
||||||
|
|
||||||
|
expect(supabaseApiRequest).toHaveBeenCalledWith(
|
||||||
|
'POST',
|
||||||
|
'/signups',
|
||||||
|
[{ email: 'test@example.com', phone: '+1234567890' }],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(result[0][0].json).toEqual({
|
||||||
|
email: 'test@example.com',
|
||||||
|
phone: '+1234567890',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a row without returning data when returnData is false', async () => {
|
||||||
|
const supabaseApiRequest = jest.spyOn(utils, 'supabaseApiRequest').mockResolvedValue({});
|
||||||
|
|
||||||
|
const fakeExecuteFunction = createMockExecuteFunction({
|
||||||
|
resource: 'row',
|
||||||
|
operation: 'create',
|
||||||
|
tableId: 'signups',
|
||||||
|
dataToSend: 'autoMapInputData',
|
||||||
|
inputsToIgnore: '',
|
||||||
|
returnData: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await node.execute.call(fakeExecuteFunction);
|
||||||
|
|
||||||
|
expect(supabaseApiRequest).toHaveBeenCalledWith(
|
||||||
|
'POST',
|
||||||
|
'/signups',
|
||||||
|
[{ email: 'test@example.com', phone: '+1234567890' }],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(result[0][0].json).toEqual({
|
||||||
|
success: true,
|
||||||
|
message: 'Row inserted',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should coerce non-boolean returnData to boolean and proceed', async () => {
|
||||||
|
const supabaseApiRequest = jest
|
||||||
|
.spyOn(utils, 'supabaseApiRequest')
|
||||||
|
.mockResolvedValue([{ email: 'test@example.com', phone: '+1234567890' }]);
|
||||||
|
|
||||||
|
const fakeExecuteFunction = createMockExecuteFunction({
|
||||||
|
resource: 'row',
|
||||||
|
operation: 'create',
|
||||||
|
tableId: 'signups',
|
||||||
|
dataToSend: 'autoMapInputData',
|
||||||
|
inputsToIgnore: '',
|
||||||
|
returnData: 'hello', // Non-boolean expression result
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await node.execute.call(fakeExecuteFunction);
|
||||||
|
|
||||||
|
// 'hello' is truthy, so expect returnData to be treated as true
|
||||||
|
expect(supabaseApiRequest).toHaveBeenCalledWith(
|
||||||
|
'POST',
|
||||||
|
'/signups',
|
||||||
|
[{ email: 'test@example.com', phone: '+1234567890' }],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
'hello', // Passed as-is, but coerced to true in JS
|
||||||
|
);
|
||||||
|
expect(result[0][0].json).toEqual({
|
||||||
|
email: 'test@example.com',
|
||||||
|
phone: '+1234567890',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue