From 176538e5f21f14ea3e5964dbe905fe4af89faaef Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:19:45 +0300 Subject: [PATCH] feat(Google Cloud Realtime Database Node): Make it possible to select region (#3096) * upstream merge * :hammer: fixed bug, replaced icon with svg, added ability to get whole db object * :hammer: optimization * :hammer: option for region in credentials * :bug: Fix region default * :zap: Remove dot Co-authored-by: ricardo Co-authored-by: Jan Oberhauser --- ...seRealtimeDatabaseOAuth2Api.credentials.ts | 20 ++++++++ .../RealtimeDatabase/GenericFunctions.ts | 9 ++-- .../RealtimeDatabase/RealtimeDatabase.node.ts | 47 +++++++++++++++--- .../googleFirebaseRealtimeDatabase.png | Bin 2103 -> 0 bytes .../googleFirebaseRealtimeDatabase.svg | 36 ++++++++++++++ 5 files changed, 101 insertions(+), 11 deletions(-) delete mode 100644 packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/googleFirebaseRealtimeDatabase.png create mode 100644 packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/googleFirebaseRealtimeDatabase.svg diff --git a/packages/nodes-base/credentials/GoogleFirebaseRealtimeDatabaseOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleFirebaseRealtimeDatabaseOAuth2Api.credentials.ts index 2e6017b296..52413c0b7e 100644 --- a/packages/nodes-base/credentials/GoogleFirebaseRealtimeDatabaseOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GoogleFirebaseRealtimeDatabaseOAuth2Api.credentials.ts @@ -23,5 +23,25 @@ export class GoogleFirebaseRealtimeDatabaseOAuth2Api implements ICredentialType type: 'hidden', 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', + }, + ], + }, ]; } diff --git a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts index acb50b22f4..3a70524f24 100644 --- a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts @@ -9,11 +9,13 @@ import { } from 'n8n-core'; import { - IDataObject, NodeApiError, + IDataObject, JsonObject, NodeApiError, } 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 { // tslint:disable-line:no-any + const { region } = await this.getCredentials('googleFirebaseRealtimeDatabaseOAuth2Api') as IDataObject; + const options: OptionsWithUrl = { headers: { 'Content-Type': 'application/json', @@ -21,9 +23,10 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF method, body, qs, - url: uri || `https://${projectId}.firebaseio.com/${resource}.json`, + url: uri || `https://${projectId}.${region}/${resource}.json`, json: true, }; + try { if (Object.keys(headers).length !== 0) { 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); } catch (error) { - throw new NodeApiError(this.getNode(), error); + throw new NodeApiError(this.getNode(), error as JsonObject); } } diff --git a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/RealtimeDatabase.node.ts b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/RealtimeDatabase.node.ts index 023d2b1a80..cd7cd66ded 100644 --- a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/RealtimeDatabase.node.ts +++ b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/RealtimeDatabase.node.ts @@ -9,6 +9,7 @@ import { INodePropertyOptions, INodeType, INodeTypeDescription, + JsonObject, NodeApiError, NodeOperationError, } from 'n8n-workflow'; @@ -22,7 +23,7 @@ export class RealtimeDatabase implements INodeType { description: INodeTypeDescription = { displayName: 'Google Cloud Realtime Database', name: 'googleFirebaseRealtimeDatabase', - icon: 'file:googleFirebaseRealtimeDatabase.png', + icon: 'file:googleFirebaseRealtimeDatabase.svg', group: ['input'], version: 1, subtitle: '={{$parameter["operation"]}}', @@ -53,6 +54,7 @@ export class RealtimeDatabase implements INodeType { displayName: 'Operation', name: 'operation', type: 'options', + noDataExpression: true, options: [ { name: 'Create', @@ -81,7 +83,6 @@ export class RealtimeDatabase implements INodeType { }, ], default: 'create', - description: 'The operation to perform.', required: true, }, { @@ -89,9 +90,28 @@ export class RealtimeDatabase implements INodeType { name: 'path', type: 'string', default: '', - placeholder: '/app/users', - description: 'Object path on database. With leading slash. Do not append .json.', + placeholder: 'e.g. /app/users', + description: 'Object path on database. Do not append .json.', 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', @@ -121,7 +141,7 @@ export class RealtimeDatabase implements INodeType { ): Promise { const projects = await googleApiRequestAllItems.call( this, - 'projects', + '', 'GET', 'results', {}, @@ -129,14 +149,23 @@ export class RealtimeDatabase implements INodeType { {}, '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; }, }, }; async execute(this: IExecuteFunctions): Promise { - const items = this.getInputData(); const returnData: IDataObject[] = []; 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; //https://firebase.google.com/docs/reference/rest/database + 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`); } @@ -151,6 +181,7 @@ export class RealtimeDatabase implements INodeType { for (let i = 0; i < length; i++) { try { const projectId = this.getNodeParameter('projectId', i) as string; + let method = 'GET', attributes = ''; const document: IDataObject = {}; if (operation === 'create') { @@ -194,7 +225,7 @@ export class RealtimeDatabase implements INodeType { } } catch (error) { if (this.continueOnFail()) { - returnData.push({ error: error.message }); + returnData.push({ error: (error as JsonObject).message }); continue; } throw error; diff --git a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/googleFirebaseRealtimeDatabase.png b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/googleFirebaseRealtimeDatabase.png deleted file mode 100644 index a95667610749659b31f1395f467b1a41d59ad878..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2103 zcmX9;3pmql8=tdyajDSZqa4bqdR0gc898iDTO%RfR3;jq(`J%6Ha-+lFU=_>Qc6h9 zWDPTmJT+eeo_w&2&`*$Cn=Xx^H9?nV%`xIa>m=Y4~QPKy`$ufIcrV3oYt&gjAa?a8a8B1cG!Fw*v|Ed0)C77ZVdB z?r`KM|M4%+X3v=z0s$CwSWpla>~&8}xG8*cQdng0I0pyP-GL!Hs03lf2_6wP)8rK&M_BI)0^Xm6 zCPWJ*BcRy|XlH~>_vZyTLAMRSLI59JL8=R+1>d>KlZJ7;q2-uU@^nHDpI561N7ndq^UYgzeV+Bq?yvl24KV2i!r9 zqnLSypQ6f7RRsIo__tKo*4BoHhx7CEaX1{<;|flk<))I|6-u{lF14IOqXFH%mo=-AHb)qmW_GPm$**$;{5qGMUVto}S#?+*_$h z$w^7^H{xPr30N!^{D1<7;NW|!t@lePc;_jzMl->>l>DRa|{-2t?MNG^>D_Yp^0nR$`%I!sMmZBO9L7~1N zUz<-><=ueI&(C*ub~ZINg-6AL?5NJixTpD{y_J^+pZky2`d~c0mODJeweG7>=r%hZ z#ah^C#>>zIbN2Je>h%O?edyiC<1rqdFiB~7X&HboB@}G!kP~eRWJCfnAR1>}R8tU% zHnKOabm3gRqe}{FKTL@J2oy8z!5C;Dz*px!GQVY&*^O+Dt8(Mq-JT#%Oj>{W=fL)WjRd)HuWoN z?YsZd9I0%L)aCi};aeHn3X#&?XsfY8S1m&MUSB(u@Q8@;@wCD!jGenS?ljCdv$4Ie zHPL^JqVS-w9)mEm-lzHT0Dt^(v_ZDkbg~Kfe$l?d1!KuvCf{V@1h5};3v7PpxJ%ms z<4_s6>+JdvT$^*EAOQ8;sbOqa+M&JHe}VSJJNA${z13*;az-FI&NEJSXq*hEWhWP% z_mlj)F|4}4!TChMx2e-f}rk#!Kizf7F5%X{DA?tMyQpLc#ktf8^AL1R{Va(TN! zA>7C3)M#u&GbS@Q@gUM}wNL5$ruvi@85a|BTb5_PH54?b_&uEy zXm}wk?cxo8%9fkl8ODwoKyoU*D6+FM4}bY9rkp}$<;Y&L*iV2xlbdkTZRI{Xp4{77 zG4-~e<*?&x+k@&si=~`u2ZO0B4lUr`%Dm2zrWKX{3Z>77am`DX*EPI!RH6bR(=k5| zzm~2FGQT8vUH8-Y<@~IcCEAp`NO)R;uc1<>ReT;kXH@;ZtrO6#l0Z5_m?@~H7e^{< zVQwr}rK*`6lU44l!kLEgS)Fa1fulD3dG!(>aH96t#41X9RHOUpUvl>-pO5K1rav4* z?b+$ZDXXoGdeg0LssJCYRl_nue%Nalhqu*gs2Dq!XTMr?``sJ;MAFpnVBz1_O!fr( zW@s5(^<@*p;vMsGlWieK>Xe)$I!?cK)2;dVMy9*FY@FR5-?-+}eEeYH?kZhB_^aom z>Shw%C3zz+q822iQX(0TM((br5tCEabB~uY7k8>#9+#k}zy_3^Do=d21@Er~N-3K< TolD#P31LV_4~JU&z|?;LSnq0; diff --git a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/googleFirebaseRealtimeDatabase.svg b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/googleFirebaseRealtimeDatabase.svg new file mode 100644 index 0000000000..feec452fc2 --- /dev/null +++ b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/googleFirebaseRealtimeDatabase.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +