Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Baserow Node): Add uploading files & trigger node #10219

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
430 changes: 273 additions & 157 deletions packages/nodes-base/nodes/Baserow/Baserow.node.ts

Large diffs are not rendered by default.

103 changes: 79 additions & 24 deletions packages/nodes-base/nodes/Baserow/GenericFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,57 @@
import type {
IDataObject,
IExecuteFunctions,
IHookFunctions,
IHttpRequestMethods,
IHttpRequestOptions,
ILoadOptionsFunctions,
IRequestOptions,
IPollFunctions,
JsonObject,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';

import type { Accumulator, BaserowCredentials, LoadedResource } from './types';

export async function baserowFileUploadRequest(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
jwtToken: string,
file: Buffer,
fileName: string,
mimeType: string,
) {
const credentials = await this.getCredentials<BaserowCredentials>('baserowApi');

const options: IHttpRequestOptions = {
headers: {
Authorization: `JWT ${jwtToken}`,
'Content-Type': 'multipart/form-data',
},
method: 'POST',
url: `${credentials.host}/api/user-files/upload-file/`,
json: false,
body: {
file: {
value: file,
options: {
filename: fileName,
contentType: mimeType,
},
},
},
};

try {
return await this.helpers.httpRequest(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}

/**
* Make a request to Baserow API.
*/
export async function baserowApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions,
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
method: IHttpRequestMethods,
endpoint: string,
jwtToken: string,
Expand All @@ -23,17 +60,26 @@ export async function baserowApiRequest(
) {
const credentials = await this.getCredentials<BaserowCredentials>('baserowApi');

const options: IRequestOptions = {
const options: IHttpRequestOptions = {
headers: {
Authorization: `JWT ${jwtToken}`,
},
method,
body,
qs,
uri: `${credentials.host}${endpoint}`,
url: `${credentials.host}${endpoint}`,
json: true,
};

if (body.formData) {
options.json = false;
options.headers = {
...options.headers,
'Content-Type': 'multipart/form-data',
};
options.returnFullResponse = true;
}

if (Object.keys(qs).length === 0) {
delete options.qs;
}
Expand All @@ -43,7 +89,7 @@ export async function baserowApiRequest(
}

try {
return await this.helpers.request(options);
return await this.helpers.httpRequest(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
Expand All @@ -53,27 +99,28 @@ export async function baserowApiRequest(
* Get all results from a paginated query to Baserow API.
*/
export async function baserowApiRequestAllItems(
this: IExecuteFunctions,
this: IExecuteFunctions | IPollFunctions,
method: IHttpRequestMethods,
endpoint: string,
jwtToken: string,
body: IDataObject,
qs: IDataObject = {},
returnAll: boolean = false,
limit: number = 0,
): Promise<IDataObject[]> {
const returnData: IDataObject[] = [];
let responseData;

qs.page = 1;
qs.size = 100;

const returnAll = this.getNodeParameter('returnAll', 0, false);
const limit = this.getNodeParameter('limit', 0, 0);
if (!qs.size) {
qs.size = 100;
}

do {
responseData = await baserowApiRequest.call(this, method, endpoint, jwtToken, body, qs);
returnData.push(...(responseData.results as IDataObject[]));

if (!returnAll && returnData.length > limit) {
if (!returnAll && limit > 0 && returnData.length > limit) {
return returnData.slice(0, limit);
}

Expand All @@ -87,21 +134,21 @@ export async function baserowApiRequestAllItems(
* Get a JWT token based on Baserow account username and password.
*/
export async function getJwtToken(
this: IExecuteFunctions | ILoadOptionsFunctions,
this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
{ username, password, host }: BaserowCredentials,
) {
const options: IRequestOptions = {
const options: IHttpRequestOptions = {
method: 'POST',
body: {
username,
password,
},
uri: `${host}/api/user/token-auth/`,
url: `${host}/api/user/token-auth/`,
json: true,
};

try {
const { token } = (await this.helpers.request(options)) as { token: string };
const { token } = (await this.helpers.httpRequest(options)) as { token: string };
return token;
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
Expand All @@ -127,6 +174,15 @@ export async function getFieldNamesAndIds(
};
}

export async function getTableFields(
this: IExecuteFunctions | IPollFunctions,
table: string,
jwtToken: string,
): Promise<LoadedResource[]> {
const endpoint = `/api/database/fields/table/${table}/`;
return await baserowApiRequest.call(this, 'GET', endpoint, jwtToken);
}

export const toOptions = (items: LoadedResource[]) =>
items.map(({ name, id }) => ({ name, value: id }));

Expand All @@ -140,15 +196,6 @@ export class TableFieldMapper {

mapIds = true;

async getTableFields(
this: IExecuteFunctions,
table: string,
jwtToken: string,
): Promise<LoadedResource[]> {
const endpoint = `/api/database/fields/table/${table}/`;
return await baserowApiRequest.call(this, 'GET', endpoint, jwtToken);
}

createMappings(tableFields: LoadedResource[]) {
this.nameToIdMapping = this.createNameToIdMapping(tableFields);
this.idToNameMapping = this.createIdToNameMapping(tableFields);
Expand Down Expand Up @@ -181,6 +228,10 @@ export class TableFieldMapper {
});
}

idToName(id: string) {
return this.idToNameMapping[id] ?? id;
}

namesToIds(obj: Record<string, unknown>) {
Object.entries(obj).forEach(([key, value]) => {
if (this.nameToIdMapping[key] !== undefined) {
Expand All @@ -189,4 +240,8 @@ export class TableFieldMapper {
}
});
}

nameToId(name: string) {
return this.nameToIdMapping[name] ?? name;
}
}
38 changes: 38 additions & 0 deletions packages/nodes-base/nodes/Baserow/OperationDescription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export const operationFields: INodeProperties[] = [
// ----------------------------------
{
displayName: 'Database Name or ID',
displayOptions: {
show: {
resource: ['row'],
},
},
name: 'databaseId',
type: 'options',
default: '',
Expand All @@ -18,6 +23,11 @@ export const operationFields: INodeProperties[] = [
},
{
displayName: 'Table Name or ID',
displayOptions: {
show: {
resource: ['row'],
},
},
name: 'tableId',
type: 'options',
default: '',
Expand Down Expand Up @@ -442,4 +452,32 @@ export const operationFields: INodeProperties[] = [
},
],
},
{
displayName: 'File',
displayOptions: {
show: {
resource: ['file'],
operation: ['upload'],
},
},
name: 'file',
type: 'resourceLocator',
default: '',
required: true,
description: 'The file to upload',
},
{
displayName: 'File URL',
displayOptions: {
show: {
resource: ['file'],
operation: ['upload-via-url'],
},
},
name: 'url',
type: 'string',
default: '',
required: true,
description: 'The URL of the file to upload',
},
];
Loading