n8n/packages/nodes-base/nodes/Baserow/GenericFunctions.ts
Iván Ovejero b058aee6c1
Add Baserow node (#1938)
* Add Baserow node

*  Add JWT method to credentials

*  Refactor Baserow node

* 🔨 Refactor to add continueOnFail

*  Extract table ID from URL

* ✏️ Reword descriptions per feedback

* 🔥 Remove API token auth per feedback

* 🔨 Reformat for readability

* 💡 Fix issues identified by nodelinter

*  Add columns param to create and update

*  Refactor JWT token retrieval

*  Add resource loaders

*  Improvements

*  Improve types

*  Clean up descriptions and comments

*  Make minor UX improvements

*  Update input data description

* 🔨 Refactor data to send for create and update

*  Add text to description

*  Small improvements

*  Change parameter names and descriptions

Co-authored-by: Jeremie Pardou-Piquemal <571533+jrmi@users.noreply.github.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
2021-07-12 13:26:21 +02:00

199 lines
4.5 KiB
TypeScript

import {
IExecuteFunctions,
} from 'n8n-core';
import {
OptionsWithUri,
} from 'request';
import {
IDataObject,
ILoadOptionsFunctions,
NodeApiError,
NodeOperationError,
} from 'n8n-workflow';
import {
Accumulator,
BaserowCredentials,
LoadedResource,
} from './types';
/**
* Make a request to Baserow API.
*/
export async function baserowApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions,
method: string,
endpoint: string,
body: IDataObject = {},
qs: IDataObject = {},
jwtToken: string,
) {
const credentials = this.getCredentials('baserowApi') as BaserowCredentials;
if (credentials === undefined) {
throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
}
const options: OptionsWithUri = {
headers: {
Authorization: `JWT ${jwtToken}`,
},
method,
body,
qs,
uri: `${credentials.host}${endpoint}`,
json: true,
};
if (Object.keys(qs).length === 0) {
delete options.qs;
}
if (Object.keys(body).length === 0) {
delete options.body;
}
try {
return await this.helpers.request!(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error);
}
}
/**
* Get all results from a paginated query to Baserow API.
*/
export async function baserowApiRequestAllItems(
this: IExecuteFunctions,
method: string,
endpoint: string,
body: IDataObject,
qs: IDataObject = {},
jwtToken: string,
): Promise<IDataObject[]> {
const returnData: IDataObject[] = [];
let responseData;
qs.page = 1;
qs.size = 100;
const returnAll = this.getNodeParameter('returnAll', 0, false) as boolean;
const limit = this.getNodeParameter('limit', 0, 0) as number;
do {
responseData = await baserowApiRequest.call(this, method, endpoint, body, qs, jwtToken);
returnData.push(...responseData.results);
if (!returnAll && returnData.length > limit) {
return returnData.slice(0, limit);
}
qs.page += 1;
} while (responseData.next !== null);
return returnData;
}
/**
* Get a JWT token based on Baserow account username and password.
*/
export async function getJwtToken(
this: IExecuteFunctions | ILoadOptionsFunctions,
{ username, password, host }: BaserowCredentials,
) {
const options: OptionsWithUri = {
method: 'POST',
body: {
username,
password,
},
uri: `${host}/api/user/token-auth/`,
json: true,
};
try {
const { token } = await this.helpers.request!(options) as { token: string };
return token;
} catch (error) {
throw new NodeApiError(this.getNode(), error);
}
}
export async function getFieldNamesAndIds(
this: IExecuteFunctions,
tableId: string,
jwtToken: string,
) {
const endpoint = `/api/database/fields/table/${tableId}/`;
const response = await baserowApiRequest.call(this, 'GET', endpoint, {}, {}, jwtToken) as LoadedResource[];
return {
names: response.map((field) => field.name),
ids: response.map((field) => `field_${field.id}`),
};
}
export const toOptions = (items: LoadedResource[]) =>
items.map(({ name, id }) => ({ name, value: id }));
/**
* Responsible for mapping field IDs `field_n` to names and vice versa.
*/
export class TableFieldMapper {
nameToIdMapping: Record<string, string> = {};
idToNameMapping: Record<string, string> = {};
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);
}
private createIdToNameMapping(responseData: LoadedResource[]) {
return responseData.reduce<Accumulator>((acc, cur) => {
acc[`field_${cur.id}`] = cur.name;
return acc;
}, {});
}
private createNameToIdMapping(responseData: LoadedResource[]) {
return responseData.reduce<Accumulator>((acc, cur) => {
acc[cur.name] = `field_${cur.id}`;
return acc;
}, {});
}
setField(field: string) {
return this.mapIds ? field : this.nameToIdMapping[field] ?? field;
}
idsToNames(obj: Record<string, unknown>) {
Object.entries(obj).forEach(([key, value]) => {
if (this.idToNameMapping[key] !== undefined) {
delete obj[key];
obj[this.idToNameMapping[key]] = value;
}
});
}
namesToIds(obj: Record<string, unknown>) {
Object.entries(obj).forEach(([key, value]) => {
if (this.nameToIdMapping[key] !== undefined) {
delete obj[key];
obj[this.nameToIdMapping[key]] = value;
}
});
}
}