mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
feat(Google Cloud Realtime Database Node): Make it possible to select region (#3096)
* upstream merge * 🔨 fixed bug, replaced icon with svg, added ability to get whole db object * 🔨 optimization * 🔨 option for region in credentials * 🐛 Fix region default * ⚡ Remove dot Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
3e5d981f3f
commit
176538e5f2
|
@ -23,5 +23,25 @@ export class GoogleFirebaseRealtimeDatabaseOAuth2Api implements ICredentialType
|
||||||
type: 'hidden',
|
type: 'hidden',
|
||||||
default: scopes.join(' '),
|
default: scopes.join(' '),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Region',
|
||||||
|
name: 'region',
|
||||||
|
type: 'options',
|
||||||
|
default: 'firebaseio.com',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'us-central1',
|
||||||
|
value: 'firebaseio.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'europe-west1',
|
||||||
|
value: 'europe-west1.firebasedatabase.app',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'asia-southeast1',
|
||||||
|
value: 'asia-southeast1.firebasedatabase.app',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,13 @@ import {
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IDataObject, NodeApiError,
|
IDataObject, JsonObject, NodeApiError,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, projectId: string, method: string, resource: string, body: any = {}, qs: IDataObject = {}, headers: IDataObject = {}, uri: string | null = null): Promise<any> { // tslint:disable-line:no-any
|
export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, projectId: string, method: string, resource: string, body: any = {}, qs: IDataObject = {}, headers: IDataObject = {}, uri: string | null = null): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const { region } = await this.getCredentials('googleFirebaseRealtimeDatabaseOAuth2Api') as IDataObject;
|
||||||
|
|
||||||
const options: OptionsWithUrl = {
|
const options: OptionsWithUrl = {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
@ -21,9 +23,10 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
||||||
method,
|
method,
|
||||||
body,
|
body,
|
||||||
qs,
|
qs,
|
||||||
url: uri || `https://${projectId}.firebaseio.com/${resource}.json`,
|
url: uri || `https://${projectId}.${region}/${resource}.json`,
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (Object.keys(headers).length !== 0) {
|
if (Object.keys(headers).length !== 0) {
|
||||||
options.headers = Object.assign({}, options.headers, headers);
|
options.headers = Object.assign({}, options.headers, headers);
|
||||||
|
@ -34,7 +37,7 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
||||||
|
|
||||||
return await this.helpers.requestOAuth2!.call(this, 'googleFirebaseRealtimeDatabaseOAuth2Api', options);
|
return await this.helpers.requestOAuth2!.call(this, 'googleFirebaseRealtimeDatabaseOAuth2Api', options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new NodeApiError(this.getNode(), error);
|
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
|
JsonObject,
|
||||||
NodeApiError,
|
NodeApiError,
|
||||||
NodeOperationError,
|
NodeOperationError,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
@ -22,7 +23,7 @@ export class RealtimeDatabase implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Google Cloud Realtime Database',
|
displayName: 'Google Cloud Realtime Database',
|
||||||
name: 'googleFirebaseRealtimeDatabase',
|
name: 'googleFirebaseRealtimeDatabase',
|
||||||
icon: 'file:googleFirebaseRealtimeDatabase.png',
|
icon: 'file:googleFirebaseRealtimeDatabase.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{$parameter["operation"]}}',
|
subtitle: '={{$parameter["operation"]}}',
|
||||||
|
@ -53,6 +54,7 @@ export class RealtimeDatabase implements INodeType {
|
||||||
displayName: 'Operation',
|
displayName: 'Operation',
|
||||||
name: 'operation',
|
name: 'operation',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
|
noDataExpression: true,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Create',
|
name: 'Create',
|
||||||
|
@ -81,7 +83,6 @@ export class RealtimeDatabase implements INodeType {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: 'create',
|
default: 'create',
|
||||||
description: 'The operation to perform.',
|
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -89,9 +90,28 @@ export class RealtimeDatabase implements INodeType {
|
||||||
name: 'path',
|
name: 'path',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
placeholder: '/app/users',
|
placeholder: 'e.g. /app/users',
|
||||||
description: 'Object path on database. With leading slash. Do not append .json.',
|
description: 'Object path on database. Do not append .json.',
|
||||||
required: true,
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
hide: {
|
||||||
|
'operation': [ 'get' ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Object Path',
|
||||||
|
name: 'path',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'e.g. /app/users',
|
||||||
|
description: 'Object path on database. Do not append .json.',
|
||||||
|
hint: 'Leave blank to get a whole database object',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'operation': [ 'get' ],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Columns / Attributes',
|
displayName: 'Columns / Attributes',
|
||||||
|
@ -121,7 +141,7 @@ export class RealtimeDatabase implements INodeType {
|
||||||
): Promise<INodePropertyOptions[]> {
|
): Promise<INodePropertyOptions[]> {
|
||||||
const projects = await googleApiRequestAllItems.call(
|
const projects = await googleApiRequestAllItems.call(
|
||||||
this,
|
this,
|
||||||
'projects',
|
'',
|
||||||
'GET',
|
'GET',
|
||||||
'results',
|
'results',
|
||||||
{},
|
{},
|
||||||
|
@ -129,14 +149,23 @@ export class RealtimeDatabase implements INodeType {
|
||||||
{},
|
{},
|
||||||
'https://firebase.googleapis.com/v1beta1/projects',
|
'https://firebase.googleapis.com/v1beta1/projects',
|
||||||
);
|
);
|
||||||
const returnData = projects.map((o: IDataObject) => ({ name: o.projectId, value: o.projectId })) as INodePropertyOptions[];
|
|
||||||
|
const returnData = projects
|
||||||
|
// select only realtime database projects
|
||||||
|
.filter((project: IDataObject) => (project.resources as IDataObject).realtimeDatabaseInstance )
|
||||||
|
.map((project: IDataObject) => (
|
||||||
|
{
|
||||||
|
name: project.projectId,
|
||||||
|
value: (project.resources as IDataObject).realtimeDatabaseInstance,
|
||||||
|
}
|
||||||
|
)) as INodePropertyOptions[];
|
||||||
|
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
const returnData: IDataObject[] = [];
|
const returnData: IDataObject[] = [];
|
||||||
const length = (items.length as unknown) as number;
|
const length = (items.length as unknown) as number;
|
||||||
|
@ -144,6 +173,7 @@ export class RealtimeDatabase implements INodeType {
|
||||||
const operation = this.getNodeParameter('operation', 0) as string;
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
//https://firebase.google.com/docs/reference/rest/database
|
//https://firebase.google.com/docs/reference/rest/database
|
||||||
|
|
||||||
|
|
||||||
if (['push', 'create', 'update'].includes(operation) && items.length === 1 && Object.keys(items[0].json).length === 0) {
|
if (['push', 'create', 'update'].includes(operation) && items.length === 1 && Object.keys(items[0].json).length === 0) {
|
||||||
throw new NodeOperationError(this.getNode(), `The ${operation} operation needs input data`);
|
throw new NodeOperationError(this.getNode(), `The ${operation} operation needs input data`);
|
||||||
}
|
}
|
||||||
|
@ -151,6 +181,7 @@ export class RealtimeDatabase implements INodeType {
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
try {
|
try {
|
||||||
const projectId = this.getNodeParameter('projectId', i) as string;
|
const projectId = this.getNodeParameter('projectId', i) as string;
|
||||||
|
|
||||||
let method = 'GET', attributes = '';
|
let method = 'GET', attributes = '';
|
||||||
const document: IDataObject = {};
|
const document: IDataObject = {};
|
||||||
if (operation === 'create') {
|
if (operation === 'create') {
|
||||||
|
@ -194,7 +225,7 @@ export class RealtimeDatabase implements INodeType {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.continueOnFail()) {
|
if (this.continueOnFail()) {
|
||||||
returnData.push({ error: error.message });
|
returnData.push({ error: (error as JsonObject).message });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,36 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192">
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path d="M143.41 47.34a4 4 0 0 0-6.77-2.16L115.88 66 99.54 34.89a4 4 0 0 0-7.08 0l-8.93 17-22.4-41.77a4 4 0 0 0-7.48 1.28L32 150l57.9 32.46a12 12 0 0 0 11.7 0L160 150z" fill="none"/>
|
||||||
|
</clipPath>
|
||||||
|
<linearGradient x1="56.9" y1="102.54" x2="48.9" y2="98.36" gradientUnits="userSpaceOnUse" id="b">
|
||||||
|
<stop offset="0" stop-color="#a52714"/>
|
||||||
|
<stop offset=".4" stop-color="#a52714" stop-opacity=".5"/>
|
||||||
|
<stop offset=".8" stop-color="#a52714" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient x1="90.89" y1="90.91" x2="87.31" y2="87.33" gradientUnits="userSpaceOnUse" id="c">
|
||||||
|
<stop offset="0" stop-color="#a52714" stop-opacity=".8"/>
|
||||||
|
<stop offset=".5" stop-color="#a52714" stop-opacity=".21"/>
|
||||||
|
<stop offset="1" stop-color="#a52714" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient x1="27.188" y1="40.281" x2="160.875" y2="173.968" gradientUnits="userSpaceOnUse" id="d">
|
||||||
|
<stop offset="0" stop-color="#fff" stop-opacity=".1"/>
|
||||||
|
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g fill="none">
|
||||||
|
<path d="M0 0h192v192H0z"/>
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path d="M32 150L53.66 11.39a4 4 0 0 1 7.48-1.27l22.4 41.78 8.93-17a4 4 0 0 1 7.08 0L160 150z" fill="#ffa000"/>
|
||||||
|
<path d="M106 9L0 0v192l32-42z" fill="url(#b)" opacity=".12"/>
|
||||||
|
<path d="M106.83 96.01l-23.3-44.12L32 150z" fill="#f57c00"/>
|
||||||
|
<path d="M0 0h192v192H0z" fill="url(#c)" opacity=".2"/>
|
||||||
|
<path d="M160 150L143.41 47.34a4 4 0 0 0-6.77-2.16L32 150l57.9 32.47a12 12 0 0 0 11.7 0z" fill="#ffca28"/>
|
||||||
|
<path d="M143.41 47.34a4 4 0 0 0-6.77-2.16L115.88 66 99.54 34.89a4 4 0 0 0-7.08 0l-8.93 17-22.4-41.77a4 4 0 0 0-7.48 1.28L32 150h-.08l.07.08.57.28L115.83 67l20.78-20.8a4 4 0 0 1 6.78 2.16l16.45 101.74.16-.1zM32.19 149.81L53.66 12.39a4 4 0 0 1 7.48-1.28l22.4 41.78 8.93-17a4 4 0 0 1 7.08 0l16 30.43z" fill-opacity=".2" fill="#fff"/>
|
||||||
|
<path d="M101.6 181.49a12 12 0 0 1-11.7 0l-57.76-32.4-.14.91 57.9 32.46a12 12 0 0 0 11.7 0L160 150l-.15-.92z" fill="#a52714" opacity=".2"/>
|
||||||
|
<path d="M143.41 47.34a4 4 0 0 0-6.77-2.16L115.88 66 99.54 34.89a4 4 0 0 0-7.08 0l-8.93 17-22.4-41.77a4 4 0 0 0-7.48 1.28L32 150l57.9 32.46a12 12 0 0 0 11.7 0L160 150z" fill="url(#d)"/>
|
||||||
|
</g>
|
||||||
|
<circle cx="144" cy="144" r="40" fill="#757575"/>
|
||||||
|
<path d="M126 150h36v8.004a3.992 3.992 0 0 1-3.99 3.996h-28.02a3.998 3.998 0 0 1-3.99-3.996zm0-20.016c0-2.2 1.786-3.984 3.99-3.984h28.02c2.204 0 3.99 1.8 3.99 3.984v14.032c0 2.2-1.786 3.984-3.99 3.984h-28.02c-2.204 0-3.99-1.8-3.99-3.984zm4 .016h28v6h-28zm0 11.01c0-.56.428-1.01 1.01-1.01h1.98c.56 0 1.01.428 1.01 1.01v1.98a.994.994 0 0 1-1.01 1.01h-1.98a.994.994 0 0 1-1.01-1.01zm0 14c0-.56.428-1.01 1.01-1.01h1.98c.56 0 1.01.428 1.01 1.01v1.98a.994.994 0 0 1-1.01 1.01h-1.98a.994.994 0 0 1-1.01-1.01z" fill="#fff" fill-rule="evenodd"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
Loading…
Reference in a new issue