mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
Merge branch 'oauth-support' into feature/oauth1-support
This commit is contained in:
commit
098b05e370
|
@ -8,9 +8,16 @@ fi
|
|||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
# Got started with arguments
|
||||
shift
|
||||
exec su-exec node ./packages/cli/bin/n8n "$@"
|
||||
COMMAND=$1;
|
||||
|
||||
if [[ "$COMMAND" == "n8n" ]]; then
|
||||
shift
|
||||
exec su-exec node ./packages/cli/bin/n8n "$@"
|
||||
else
|
||||
exec su-exec node "$@"
|
||||
fi
|
||||
|
||||
else
|
||||
# Got started without arguments
|
||||
exec su-exec node ./packages/cli/bin/n8n
|
||||
# Got started without arguments
|
||||
exec su-exec node ./packages/cli/bin/n8n
|
||||
fi
|
||||
|
|
|
@ -17,6 +17,7 @@ n8n is a free and open [fair-code](http://faircode.io) licensed node based Workf
|
|||
- [Securing n8n](#securing-n8n)
|
||||
- [Persist data](#persist-data)
|
||||
- [Passing Sensitive Data via File](#passing-sensitive-data-via-file)
|
||||
- [Updating a Running docker-compose Instance](#updating-a-running-docker-compose-instance)
|
||||
- [Example Setup with Lets Encrypt](#example-setup-with-lets-encrypt)
|
||||
- [What does n8n mean and how do you pronounce it](#what-does-n8n-mean-and-how-do-you-pronounce-it)
|
||||
- [Support](#support)
|
||||
|
@ -226,6 +227,18 @@ The following environment variables support file input:
|
|||
A basic step by step example setup of n8n with docker-compose and Lets Encrypt is available on the
|
||||
[Server Setup](https://docs.n8n.io/#/server-setup) page.
|
||||
|
||||
## Updating a running docker-compose instance
|
||||
|
||||
```
|
||||
# Pull down the latest version from dockerhub
|
||||
docker pull n8nio/n8n
|
||||
# Stop current setup
|
||||
sudo docker-compose stop
|
||||
# Delete it (will only delete the docker-containers, data is stored separately)
|
||||
sudo docker-compose rm
|
||||
# Then start it again
|
||||
sudo docker-compose up -d
|
||||
```
|
||||
|
||||
## Setting Timezone
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
const scopes = [
|
||||
'https://www.googleapis.com/auth/calendar',
|
||||
'https://www.googleapis.com/auth/calendar.events',
|
||||
];
|
||||
|
||||
export class GoogleCalendarOAuth2Api implements ICredentialType {
|
||||
name = 'googleCalendarOAuth2Api';
|
||||
extends = [
|
||||
'googleOAuth2Api',
|
||||
];
|
||||
displayName = 'Google Calendar OAuth2 API';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'Scope',
|
||||
name: 'scope',
|
||||
type: 'hidden' as NodePropertyTypes,
|
||||
default: scopes.join(' '),
|
||||
},
|
||||
];
|
||||
}
|
|
@ -3,11 +3,6 @@ import {
|
|||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
const scopes = [
|
||||
'https://www.googleapis.com/auth/calendar',
|
||||
'https://www.googleapis.com/auth/calendar.events',
|
||||
];
|
||||
|
||||
export class GoogleOAuth2Api implements ICredentialType {
|
||||
name = 'googleOAuth2Api';
|
||||
extends = [
|
||||
|
@ -27,12 +22,6 @@ export class GoogleOAuth2Api implements ICredentialType {
|
|||
type: 'hidden' as NodePropertyTypes,
|
||||
default: 'https://oauth2.googleapis.com/token',
|
||||
},
|
||||
{
|
||||
displayName: 'Scope',
|
||||
name: 'scope',
|
||||
type: 'hidden' as NodePropertyTypes,
|
||||
default: scopes.join(' '),
|
||||
},
|
||||
{
|
||||
displayName: 'Auth URI Query Parameters',
|
||||
name: 'authQueryParameters',
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
const scopes = [
|
||||
'https://www.googleapis.com/auth/drive',
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
'https://www.googleapis.com/auth/spreadsheets',
|
||||
];
|
||||
|
||||
export class GoogleSheetsOAuth2Api implements ICredentialType {
|
||||
name = 'googleSheetsOAuth2Api';
|
||||
extends = [
|
||||
'googleOAuth2Api',
|
||||
];
|
||||
displayName = 'Google Sheets OAuth2 API';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'Scope',
|
||||
name: 'scope',
|
||||
type: 'hidden' as NodePropertyTypes,
|
||||
default: scopes.join(' '),
|
||||
},
|
||||
];
|
||||
}
|
|
@ -432,6 +432,80 @@ export class EditImage implements INodeType {
|
|||
},
|
||||
description: 'The color to use for the background when image gets rotated by anything which is not a multiple of 90..',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
hide: {
|
||||
operation: [
|
||||
'information',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'File Name',
|
||||
name: 'fileName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'File name to set in binary data.',
|
||||
},
|
||||
{
|
||||
displayName: 'Format',
|
||||
name: 'format',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'bmp',
|
||||
value: 'bmp',
|
||||
},
|
||||
{
|
||||
name: 'gif',
|
||||
value: 'gif',
|
||||
},
|
||||
{
|
||||
name: 'jpeg',
|
||||
value: 'jpeg',
|
||||
},
|
||||
{
|
||||
name: 'png',
|
||||
value: 'png',
|
||||
},
|
||||
{
|
||||
name: 'tiff',
|
||||
value: 'tiff',
|
||||
},
|
||||
],
|
||||
default: 'jpeg',
|
||||
description: 'Set the output image format.',
|
||||
},
|
||||
{
|
||||
displayName: 'Quality',
|
||||
name: 'quality',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 100,
|
||||
displayOptions: {
|
||||
show: {
|
||||
format: [
|
||||
'jpeg',
|
||||
'png',
|
||||
'tiff',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Sets the jpeg|png|tiff compression level from 0 to 100 (best).',
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -442,6 +516,8 @@ export class EditImage implements INodeType {
|
|||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
const dataPropertyName = this.getNodeParameter('dataPropertyName') as string;
|
||||
|
||||
const options = this.getNodeParameter('options', {}) as IDataObject;
|
||||
|
||||
// TODO: Later should make so that it sends directly a valid buffer and the buffer.from stuff is not needed anymore
|
||||
if (item.binary === undefined) {
|
||||
return item;
|
||||
|
@ -550,6 +626,24 @@ export class EditImage implements INodeType {
|
|||
Object.assign(newItem.binary, item.binary);
|
||||
}
|
||||
|
||||
if (options.quality !== undefined) {
|
||||
gmInstance = gmInstance.quality(options.quality as number);
|
||||
}
|
||||
|
||||
if (options.format !== undefined) {
|
||||
gmInstance = gmInstance.setFormat(options.format as string);
|
||||
newItem.binary![dataPropertyName as string].fileExtension = options.format as string;
|
||||
newItem.binary![dataPropertyName as string].mimeType = `image/${options.format}`;
|
||||
const fileName = newItem.binary![dataPropertyName as string].fileName;
|
||||
if (fileName && fileName.includes('.')) {
|
||||
newItem.binary![dataPropertyName as string].fileName = fileName.split('.').slice(0, -1).join('.') + '.' + options.format;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.fileName !== undefined) {
|
||||
newItem.binary![dataPropertyName as string].fileName = options.fileName as string;
|
||||
}
|
||||
|
||||
return new Promise<INodeExecutionData>((resolve, reject) => {
|
||||
gmInstance
|
||||
.toBuffer((error: Error | null, buffer: Buffer) => {
|
||||
|
|
|
@ -47,7 +47,7 @@ export class Github implements INodeType {
|
|||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'oauth2',
|
||||
'oAuth2',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -65,7 +65,7 @@ export class Github implements INodeType {
|
|||
},
|
||||
{
|
||||
name: 'OAuth2',
|
||||
value: 'oauth2',
|
||||
value: 'oAuth2',
|
||||
},
|
||||
],
|
||||
default: 'accessToken',
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import { OptionsWithUri } from 'request';
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
@ -27,7 +31,7 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
|||
delete options.body;
|
||||
}
|
||||
//@ts-ignore
|
||||
return await this.helpers.requestOAuth2.call(this, 'googleOAuth2Api', options);
|
||||
return await this.helpers.requestOAuth.call(this, 'googleCalendarOAuth2Api', options);
|
||||
} catch (error) {
|
||||
if (error.response && error.response.body && error.response.body.message) {
|
||||
// Try to return the error prettier
|
|
@ -44,7 +44,7 @@ export class GoogleCalendar implements INodeType {
|
|||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'googleOAuth2Api',
|
||||
name: 'googleCalendarOAuth2Api',
|
||||
required: true,
|
||||
},
|
||||
],
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
@ -12,7 +12,7 @@ import {
|
|||
INodeType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { getAuthenticationClient } from './GoogleApi';
|
||||
import { getAuthenticationClient } from '../GoogleApi';
|
||||
|
||||
|
||||
export class GoogleDrive implements INodeType {
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
129
packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts
Normal file
129
packages/nodes-base/nodes/Google/Sheet/GenericFunctions.ts
Normal file
|
@ -0,0 +1,129 @@
|
|||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as moment from 'moment-timezone';
|
||||
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
|
||||
export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
const authenticationMethod = this.getNodeParameter('authentication', 0, 'serviceAccount') as string;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri || `https://sheets.googleapis.com${resource}`,
|
||||
json: true
|
||||
};
|
||||
try {
|
||||
if (Object.keys(headers).length !== 0) {
|
||||
options.headers = Object.assign({}, options.headers, headers);
|
||||
}
|
||||
if (Object.keys(body).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
if (authenticationMethod === 'serviceAccount') {
|
||||
const credentials = this.getCredentials('googleApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const { access_token } = await getAccessToken.call(this, credentials as IDataObject);
|
||||
|
||||
options.headers!.Authorization = `Bearer ${access_token}`;
|
||||
//@ts-ignore
|
||||
return await this.helpers.request(options);
|
||||
} else {
|
||||
//@ts-ignore
|
||||
return await this.helpers.requestOAuth2.call(this, 'googleSheetsOAuth2Api', options);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.response && error.response.body && error.response.body.message) {
|
||||
// Try to return the error prettier
|
||||
throw new Error(`Google Sheet error response [${error.statusCode}]: ${error.response.body.message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
query.maxResults = 100;
|
||||
|
||||
do {
|
||||
responseData = await googleApiRequest.call(this, method, endpoint, body, query);
|
||||
query.pageToken = responseData['nextPageToken'];
|
||||
returnData.push.apply(returnData, responseData[propertyName]);
|
||||
} while (
|
||||
responseData['nextPageToken'] !== undefined &&
|
||||
responseData['nextPageToken'] !== ''
|
||||
);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
function getAccessToken(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IDataObject) : Promise<IDataObject> {
|
||||
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
|
||||
|
||||
const scopes = [
|
||||
'https://www.googleapis.com/auth/drive',
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
'https://www.googleapis.com/auth/spreadsheets',
|
||||
];
|
||||
|
||||
const now = moment().unix();
|
||||
|
||||
const signature = jwt.sign(
|
||||
{
|
||||
'iss': credentials.email as string,
|
||||
'sub': credentials.email as string,
|
||||
'scope': scopes.join(' '),
|
||||
'aud': `https://oauth2.googleapis.com/token`,
|
||||
'iat': now,
|
||||
'exp': now + 3600,
|
||||
},
|
||||
credentials.privateKey as string,
|
||||
{
|
||||
algorithm: 'RS256',
|
||||
header: {
|
||||
'kid': credentials.privateKey as string,
|
||||
'typ': 'JWT',
|
||||
'alg': 'RS256',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
method: 'POST',
|
||||
form: {
|
||||
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
||||
assertion: signature,
|
||||
},
|
||||
uri: 'https://oauth2.googleapis.com/token',
|
||||
json: true
|
||||
};
|
||||
|
||||
//@ts-ignore
|
||||
return this.helpers.request(options);
|
||||
}
|
|
@ -1,14 +1,20 @@
|
|||
import { IDataObject } from 'n8n-workflow';
|
||||
import { google, sheets_v4 } from 'googleapis';
|
||||
import { JWT } from 'google-auth-library';
|
||||
import { getAuthenticationClient } from './GoogleApi';
|
||||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
googleApiRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
utils as xlsxUtils,
|
||||
} from 'xlsx';
|
||||
|
||||
const Sheets = google.sheets('v4'); // tslint:disable-line:variable-name
|
||||
|
||||
export interface ISheetOptions {
|
||||
scope: string[];
|
||||
}
|
||||
|
@ -46,18 +52,16 @@ export type ValueRenderOption = 'FORMATTED_VALUE' | 'FORMULA' | 'UNFORMATTED_VAL
|
|||
|
||||
export class GoogleSheet {
|
||||
id: string;
|
||||
credentials: IGoogleAuthCredentials;
|
||||
scopes: string[];
|
||||
executeFunctions: IExecuteFunctions | ILoadOptionsFunctions;
|
||||
|
||||
constructor(spreadsheetId: string, credentials: IGoogleAuthCredentials, options?: ISheetOptions | undefined) {
|
||||
constructor(spreadsheetId: string, executeFunctions: IExecuteFunctions | ILoadOptionsFunctions, options?: ISheetOptions | undefined) {
|
||||
// options = <SheetOptions>options || {};
|
||||
if (!options) {
|
||||
options = {} as ISheetOptions;
|
||||
}
|
||||
|
||||
this.executeFunctions = executeFunctions;
|
||||
this.id = spreadsheetId;
|
||||
this.credentials = credentials;
|
||||
this.scopes = options.scope || ['https://www.googleapis.com/auth/spreadsheets'];
|
||||
}
|
||||
|
||||
|
||||
|
@ -69,37 +73,29 @@ export class GoogleSheet {
|
|||
* @memberof GoogleSheet
|
||||
*/
|
||||
async clearData(range: string): Promise<object> {
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
// @ts-ignore
|
||||
const response = await Sheets.spreadsheets.values.clear(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
range,
|
||||
}
|
||||
);
|
||||
const body = {
|
||||
spreadsheetId: this.id,
|
||||
range,
|
||||
};
|
||||
|
||||
return response.data;
|
||||
const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}:clear`, body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cell values
|
||||
*/
|
||||
async getData(range: string, valueRenderMode: ValueRenderOption): Promise<string[][] | undefined> {
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
// @ts-ignore
|
||||
const response = await Sheets.spreadsheets.values.get(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
range,
|
||||
valueRenderOption: valueRenderMode,
|
||||
}
|
||||
);
|
||||
const query = {
|
||||
valueRenderOption: valueRenderMode,
|
||||
};
|
||||
|
||||
return response.data.values as string[][] | undefined;
|
||||
const response = await googleApiRequest.call(this.executeFunctions, 'GET', `/v4/spreadsheets/${this.id}/values/${range}`, {}, query);
|
||||
|
||||
return response.values as string[][] | undefined;
|
||||
}
|
||||
|
||||
|
||||
|
@ -107,39 +103,29 @@ export class GoogleSheet {
|
|||
* Returns the sheets in a Spreadsheet
|
||||
*/
|
||||
async spreadsheetGetSheets() {
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
// @ts-ignore
|
||||
const response = await Sheets.spreadsheets.get(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
fields: 'sheets.properties'
|
||||
}
|
||||
);
|
||||
const query = {
|
||||
fields: 'sheets.properties',
|
||||
};
|
||||
|
||||
return response.data;
|
||||
const response = await googleApiRequest.call(this.executeFunctions, 'GET', `/v4/spreadsheets/${this.id}`, {}, query);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets values in one or more ranges of a spreadsheet.
|
||||
*/
|
||||
async spreadsheetBatchUpdate(requests: sheets_v4.Schema$Request[]) { // tslint:disable-line:no-any
|
||||
const client = await this.getAuthenticationClient();
|
||||
async spreadsheetBatchUpdate(requests: IDataObject[]) { // tslint:disable-line:no-any
|
||||
|
||||
// @ts-ignore
|
||||
const response = await Sheets.spreadsheets.batchUpdate(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
requestBody: {
|
||||
requests,
|
||||
},
|
||||
}
|
||||
);
|
||||
const body = {
|
||||
requests
|
||||
};
|
||||
|
||||
return response.data;
|
||||
const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}:batchUpdate`, body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
@ -147,21 +133,15 @@ export class GoogleSheet {
|
|||
* Sets the cell values
|
||||
*/
|
||||
async batchUpdate(updateData: ISheetUpdateData[], valueInputMode: ValueInputOption) {
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
// @ts-ignore
|
||||
const response = await Sheets.spreadsheets.values.batchUpdate(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
valueInputOption: valueInputMode,
|
||||
resource: {
|
||||
data: updateData,
|
||||
},
|
||||
}
|
||||
);
|
||||
const body = {
|
||||
data: updateData,
|
||||
valueInputOption: valueInputMode,
|
||||
};
|
||||
|
||||
return response.data;
|
||||
const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values:batchUpdate`, body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
@ -169,23 +149,15 @@ export class GoogleSheet {
|
|||
* Sets the cell values
|
||||
*/
|
||||
async setData(range: string, data: string[][], valueInputMode: ValueInputOption) {
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
// @ts-ignore
|
||||
const response = await Sheets.spreadsheets.values.update(
|
||||
{
|
||||
// @ts-ignore
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
range,
|
||||
valueInputOption: valueInputMode,
|
||||
resource: {
|
||||
values: data
|
||||
}
|
||||
}
|
||||
);
|
||||
const body = {
|
||||
valueInputOption: valueInputMode,
|
||||
values: data,
|
||||
};
|
||||
|
||||
return response.data;
|
||||
const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}`, body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
@ -193,33 +165,21 @@ export class GoogleSheet {
|
|||
* Appends the cell values
|
||||
*/
|
||||
async appendData(range: string, data: string[][], valueInputMode: ValueInputOption) {
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
// @ts-ignore
|
||||
const response = await Sheets.spreadsheets.values.append(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
range,
|
||||
valueInputOption: valueInputMode,
|
||||
resource: {
|
||||
values: data
|
||||
}
|
||||
}
|
||||
);
|
||||
const body = {
|
||||
range,
|
||||
values: data,
|
||||
};
|
||||
|
||||
return response.data;
|
||||
const query = {
|
||||
valueInputOption: valueInputMode,
|
||||
};
|
||||
|
||||
const response = await googleApiRequest.call(this.executeFunctions, 'POST', `/v4/spreadsheets/${this.id}/values/${range}:append`, body, query);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the authentication client needed to access spreadsheet
|
||||
*/
|
||||
async getAuthenticationClient(): Promise<JWT> {
|
||||
return getAuthenticationClient(this.credentials.email, this.credentials.privateKey, this.scopes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the given sheet data in a strucutred way
|
||||
*/
|
||||
|
@ -505,5 +465,4 @@ export class GoogleSheet {
|
|||
|
||||
return setData;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
import { sheets_v4 } from 'googleapis';
|
||||
|
||||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
|
@ -12,7 +14,6 @@ import {
|
|||
|
||||
import {
|
||||
GoogleSheet,
|
||||
IGoogleAuthCredentials,
|
||||
ILookupValues,
|
||||
ISheetUpdateData,
|
||||
IToDelete,
|
||||
|
@ -30,7 +31,7 @@ export class GoogleSheets implements INodeType {
|
|||
description: 'Read, update and write data to Google Sheets',
|
||||
defaults: {
|
||||
name: 'Google Sheets',
|
||||
color: '#995533',
|
||||
color: '#0aa55c',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
|
@ -38,9 +39,43 @@ export class GoogleSheets implements INodeType {
|
|||
{
|
||||
name: 'googleApi',
|
||||
required: true,
|
||||
}
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'serviceAccount',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'googleSheetsOAuth2Api',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'oAuth2',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Authentication',
|
||||
name: 'authentication',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Service Account',
|
||||
value: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'OAuth2',
|
||||
value: 'oAuth2',
|
||||
},
|
||||
],
|
||||
default: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
|
@ -541,18 +576,7 @@ export class GoogleSheets implements INodeType {
|
|||
async getSheets(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const spreadsheetId = this.getCurrentNodeParameter('sheetId') as string;
|
||||
|
||||
const credentials = this.getCredentials('googleApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const googleCredentials = {
|
||||
email: credentials.email,
|
||||
privateKey: credentials.privateKey,
|
||||
} as IGoogleAuthCredentials;
|
||||
|
||||
const sheet = new GoogleSheet(spreadsheetId, googleCredentials);
|
||||
const sheet = new GoogleSheet(spreadsheetId, this);
|
||||
const responseData = await sheet.spreadsheetGetSheets();
|
||||
|
||||
if (responseData === undefined) {
|
||||
|
@ -579,18 +603,8 @@ export class GoogleSheets implements INodeType {
|
|||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const spreadsheetId = this.getNodeParameter('sheetId', 0) as string;
|
||||
const credentials = this.getCredentials('googleApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const googleCredentials = {
|
||||
email: credentials.email,
|
||||
privateKey: credentials.privateKey,
|
||||
} as IGoogleAuthCredentials;
|
||||
|
||||
const sheet = new GoogleSheet(spreadsheetId, googleCredentials);
|
||||
const sheet = new GoogleSheet(spreadsheetId, this);
|
||||
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
|
@ -638,7 +652,7 @@ export class GoogleSheets implements INodeType {
|
|||
// delete
|
||||
// ----------------------------------
|
||||
|
||||
const requests: sheets_v4.Schema$Request[] = [];
|
||||
const requests: IDataObject[] = [];
|
||||
|
||||
const toDelete = this.getNodeParameter('toDelete', 0) as IToDelete;
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -110,7 +110,7 @@ export class Slack implements INodeType {
|
|||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'oauth2',
|
||||
'oAuth2',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -128,7 +128,7 @@ export class Slack implements INodeType {
|
|||
},
|
||||
{
|
||||
name: 'OAuth2',
|
||||
value: 'oauth2',
|
||||
value: 'oAuth2',
|
||||
},
|
||||
],
|
||||
default: 'accessToken',
|
||||
|
|
|
@ -40,7 +40,7 @@ export class TrelloTrigger implements INodeType {
|
|||
webhooks: [
|
||||
{
|
||||
name: 'setup',
|
||||
httpMethod: 'GET',
|
||||
httpMethod: 'HEAD',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
|
|
|
@ -33,11 +33,11 @@ export const streamOperations = [
|
|||
value: 'getSubscribed',
|
||||
description: 'Get subscribed streams.',
|
||||
},
|
||||
// {
|
||||
// name: 'Update',
|
||||
// value: 'update',
|
||||
// description: 'Update a stream.',
|
||||
// },
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a stream.',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
|
|
|
@ -33,11 +33,11 @@ export const userOperations = [
|
|||
value: 'getAll',
|
||||
description: 'Get all users.',
|
||||
},
|
||||
// {
|
||||
// name: 'Update',
|
||||
// value: 'update',
|
||||
// description: 'Update a user.',
|
||||
// },
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a user.',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
|
|
|
@ -263,16 +263,16 @@ export class Zulip implements INodeType {
|
|||
|
||||
if (operation === 'create') {
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
const subscriptions = this.getNodeParameter('subscriptions', i) as IDataObject;
|
||||
|
||||
body.subscriptions = JSON.stringify(subscriptions.properties);
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
|
||||
|
||||
if (additionalFieldsJson !== '') {
|
||||
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
|
||||
} else {
|
||||
throw new Error('Additional fields must be a valid JSON');
|
||||
}
|
||||
|
@ -342,10 +342,10 @@ export class Zulip implements INodeType {
|
|||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (additionalFields.description) {
|
||||
body.description = additionalFields.description as string;
|
||||
body.description = JSON.stringify(additionalFields.description as string);
|
||||
}
|
||||
if (additionalFields.newName) {
|
||||
body.new_name = additionalFields.newName as string;
|
||||
body.new_name = JSON.stringify(additionalFields.newName as string);
|
||||
}
|
||||
if (additionalFields.isPrivate) {
|
||||
body.is_private = additionalFields.isPrivate as boolean;
|
||||
|
@ -412,7 +412,7 @@ export class Zulip implements INodeType {
|
|||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (additionalFields.fullName) {
|
||||
body.full_name = additionalFields.fullName as string;
|
||||
body.full_name = JSON.stringify(additionalFields.fullName as string);
|
||||
}
|
||||
if (additionalFields.isAdmin) {
|
||||
body.is_admin = additionalFields.isAdmin as boolean;
|
||||
|
|
|
@ -57,7 +57,9 @@
|
|||
"dist/credentials/GithubOAuth2Api.credentials.js",
|
||||
"dist/credentials/GitlabApi.credentials.js",
|
||||
"dist/credentials/GoogleApi.credentials.js",
|
||||
"dist/credentials/GoogleCalendarOAuth2Api.credentials.js",
|
||||
"dist/credentials/GoogleOAuth2Api.credentials.js",
|
||||
"dist/credentials/GoogleSheetsOAuth2Api.credentials.js",
|
||||
"dist/credentials/GumroadApi.credentials.js",
|
||||
"dist/credentials/HarvestApi.credentials.js",
|
||||
"dist/credentials/HelpScoutOAuth2Api.credentials.js",
|
||||
|
@ -182,9 +184,9 @@
|
|||
"dist/nodes/Github/GithubTrigger.node.js",
|
||||
"dist/nodes/Gitlab/Gitlab.node.js",
|
||||
"dist/nodes/Gitlab/GitlabTrigger.node.js",
|
||||
"dist/nodes/Google/GoogleCalendar.node.js",
|
||||
"dist/nodes/Google/GoogleDrive.node.js",
|
||||
"dist/nodes/Google/GoogleSheets.node.js",
|
||||
"dist/nodes/Google/Calendar/GoogleCalendar.node.js",
|
||||
"dist/nodes/Google/Drive/GoogleDrive.node.js",
|
||||
"dist/nodes/Google/Sheet/GoogleSheets.node.js",
|
||||
"dist/nodes/GraphQL/GraphQL.node.js",
|
||||
"dist/nodes/Gumroad/GumroadTrigger.node.js",
|
||||
"dist/nodes/Harvest/Harvest.node.js",
|
||||
|
@ -319,6 +321,7 @@
|
|||
"gm": "^1.23.1",
|
||||
"googleapis": "~50.0.0",
|
||||
"imap-simple": "^4.3.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.unset": "^4.5.2",
|
||||
|
|
Loading…
Reference in a new issue