-
Notifications
You must be signed in to change notification settings - Fork 11.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ✨ Supabase node * ⚡ Improvements * ⚡ Improvements * ⚡ Improvements * ⚡ Improvements * ⚡ Minor improvements Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
- Loading branch information
1 parent
5fec563
commit 8e708f3
Showing
6 changed files
with
1,033 additions
and
0 deletions.
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
packages/nodes-base/credentials/SupabaseApi.credentials.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { | ||
ICredentialType, | ||
INodeProperties, | ||
} from 'n8n-workflow'; | ||
|
||
export class SupabaseApi implements ICredentialType { | ||
name = 'supabaseApi'; | ||
displayName = 'Supabase API'; | ||
documentationUrl = 'superbase'; | ||
properties: INodeProperties[] = [ | ||
{ | ||
displayName: 'Host', | ||
name: 'host', | ||
type: 'string', | ||
placeholder: 'https://your_account.supabase.co', | ||
default: '', | ||
}, | ||
{ | ||
displayName: 'Service Role Secret', | ||
name: 'serviceRole', | ||
type: 'string', | ||
default: '', | ||
}, | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,319 @@ | ||
import { | ||
OptionsWithUri, | ||
} from 'request'; | ||
|
||
import { | ||
IExecuteFunctions, | ||
IExecuteSingleFunctions, | ||
IHookFunctions, | ||
ILoadOptionsFunctions, | ||
IWebhookFunctions, | ||
} from 'n8n-core'; | ||
|
||
import { | ||
ICredentialDataDecryptedObject, | ||
ICredentialTestFunctions, | ||
IDataObject, | ||
INodeProperties, | ||
NodeApiError, | ||
} from 'n8n-workflow'; | ||
|
||
export async function supabaseApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IHookFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any | ||
const credentials = await this.getCredentials('supabaseApi') as { host: string, serviceRole: string }; | ||
|
||
const options: OptionsWithUri = { | ||
headers: { | ||
apikey: credentials.serviceRole, | ||
Prefer: 'return=representation', | ||
}, | ||
method, | ||
qs, | ||
body, | ||
uri: uri || `${credentials.host}/rest/v1${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; | ||
} | ||
//@ts-ignore | ||
return await this.helpers?.request(options); | ||
|
||
} catch (error) { | ||
throw new NodeApiError(this.getNode(), error); | ||
} | ||
} | ||
|
||
const mapOperations: { [key: string]: string } = { | ||
'create': 'created', | ||
'update': 'updated', | ||
'getAll': 'retrieved', | ||
'delete': 'deleted', | ||
}; | ||
|
||
export function getFilters( | ||
resources: string[], | ||
operations: string[], | ||
{ | ||
includeNoneOption = true, | ||
filterTypeDisplayName = 'Filter', | ||
filterFixedCollectionDisplayName = 'Filters', | ||
filterStringDisplayName = 'Filters (String)', | ||
mustMatchOptions = [ | ||
{ | ||
name: 'Any Filter', | ||
value: 'anyFilter', | ||
}, | ||
{ | ||
name: 'All Filters', | ||
value: 'allFilters', | ||
}, | ||
], | ||
}): INodeProperties[] { | ||
return [ | ||
{ | ||
displayName: filterTypeDisplayName, | ||
name: 'filterType', | ||
type: 'options', | ||
options: [ | ||
...(includeNoneOption ? [{ name: 'None', value: 'none' }] : []), | ||
{ | ||
name: 'Build Manually', | ||
value: 'manual', | ||
}, | ||
{ | ||
name: 'String', | ||
value: 'string', | ||
}, | ||
], | ||
displayOptions: { | ||
show: { | ||
resource: resources, | ||
operation: operations, | ||
}, | ||
}, | ||
default: 'manual', | ||
}, | ||
{ | ||
displayName: 'Must Match', | ||
name: 'matchType', | ||
type: 'options', | ||
options: mustMatchOptions, | ||
displayOptions: { | ||
show: { | ||
resource: resources, | ||
operation: operations, | ||
filterType: [ | ||
'manual', | ||
], | ||
}, | ||
}, | ||
default: 'anyFilter', | ||
}, | ||
{ | ||
displayName: filterFixedCollectionDisplayName, | ||
name: 'filters', | ||
type: 'fixedCollection', | ||
typeOptions: { | ||
multipleValues: true, | ||
}, | ||
displayOptions: { | ||
show: { | ||
resource: resources, | ||
operation: operations, | ||
filterType: [ | ||
'manual', | ||
], | ||
}, | ||
}, | ||
default: '', | ||
placeholder: 'Add Condition', | ||
options: [ | ||
{ | ||
displayName: 'Conditions', | ||
name: 'conditions', | ||
values: [ | ||
{ | ||
displayName: 'Field Name', | ||
name: 'keyName', | ||
type: 'options', | ||
typeOptions: { | ||
loadOptionsDependsOn: [ | ||
'tableId', | ||
], | ||
loadOptionsMethod: 'getTableColumns', | ||
}, | ||
default: '', | ||
}, | ||
{ | ||
displayName: 'Condition', | ||
name: 'condition', | ||
type: 'options', | ||
options: [ | ||
{ | ||
name: 'Equals', | ||
value: 'eq', | ||
}, | ||
{ | ||
name: 'Greater Than', | ||
value: 'gt', | ||
}, | ||
{ | ||
name: 'Greater Than or Equal', | ||
value: 'gte', | ||
}, | ||
{ | ||
name: 'Less Than', | ||
value: 'lt', | ||
}, | ||
{ | ||
name: 'Less Than or Equal', | ||
value: 'lte', | ||
}, | ||
{ | ||
name: 'Not Equals', | ||
value: 'neq', | ||
}, | ||
{ | ||
name: 'LIKE operator', | ||
value: 'like', | ||
description: 'use * in place of %', | ||
}, | ||
{ | ||
name: 'ILIKE operator', | ||
value: 'ilike', | ||
description: 'use * in place of %', | ||
}, | ||
{ | ||
name: 'Is', | ||
value: 'is', | ||
description: 'Checking for exact equality (null,true,false,unknown)', | ||
}, | ||
{ | ||
name: 'Full-Text', | ||
value: 'fullText', | ||
}, | ||
], | ||
default: '', | ||
}, | ||
{ | ||
displayName: 'Search Function', | ||
name: 'searchFunction', | ||
type: 'options', | ||
displayOptions: { | ||
show: { | ||
condition: [ | ||
'fullText', | ||
], | ||
}, | ||
}, | ||
options: [ | ||
{ | ||
name: 'to_tsquery', | ||
value: 'fts', | ||
}, | ||
{ | ||
name: 'plainto_tsquery', | ||
value: 'plfts', | ||
}, | ||
{ | ||
name: 'phraseto_tsquery', | ||
value: 'phfts', | ||
}, | ||
{ | ||
name: 'websearch_to_tsquery', | ||
value: 'wfts', | ||
}, | ||
], | ||
default: '', | ||
}, | ||
{ | ||
displayName: 'Field Value', | ||
name: 'keyValue', | ||
type: 'string', | ||
default: '', | ||
}, | ||
], | ||
}, | ||
], | ||
description: `Filter to decide which rows get ${mapOperations[operations[0] as string]}`, | ||
}, | ||
{ | ||
displayName: 'See <a href="https://postgrest.org/en/v9.0/api.html#horizontal-filtering-rows" target="_blank">PostgREST guide</a> to creating filters', | ||
name: 'jsonNotice', | ||
type: 'notice', | ||
displayOptions: { | ||
show: { | ||
resource: resources, | ||
operation: operations, | ||
filterType: [ | ||
'string', | ||
], | ||
}, | ||
}, | ||
default: '', | ||
}, | ||
{ | ||
displayName: 'Filters (String)', | ||
name: 'filterString', | ||
type: 'string', | ||
typeOptions: { | ||
alwaysOpenEditWindow: true, | ||
}, | ||
displayOptions: { | ||
show: { | ||
resource: resources, | ||
operation: operations, | ||
filterType: [ | ||
'string', | ||
], | ||
}, | ||
}, | ||
default: '', | ||
placeholder: 'name=eq.jhon', | ||
}, | ||
]; | ||
} | ||
|
||
export const buildQuery = (obj: IDataObject, value: IDataObject) => { | ||
if (value.condition === 'fullText') { | ||
return Object.assign(obj, { [`${value.keyName}`]: `${value.searchFunction}.${value.keyValue}` }); | ||
} | ||
return Object.assign(obj, { [`${value.keyName}`]: `${value.condition}.${value.keyValue}` }); | ||
}; | ||
|
||
export const buildOrQuery = (key: IDataObject) => { | ||
if (key.condition === 'fullText') { | ||
return `${key.keyName}.${key.searchFunction}.${key.keyValue}`; | ||
} | ||
return `${key.keyName}.${key.condition}.${key.keyValue}`; | ||
}; | ||
|
||
export const buildGetQuery = (obj: IDataObject, value: IDataObject) => { | ||
return Object.assign(obj, { [`${value.keyName}`]: `eq.${value.keyValue}` }); | ||
}; | ||
|
||
export async function validateCrendentials( | ||
this: ICredentialTestFunctions, | ||
decryptedCredentials: ICredentialDataDecryptedObject): Promise<any> { // tslint:disable-line:no-any | ||
|
||
const credentials = decryptedCredentials; | ||
|
||
const { serviceRole } = credentials as { | ||
serviceRole: string, | ||
}; | ||
|
||
const options: OptionsWithUri = { | ||
headers: { | ||
apikey: serviceRole, | ||
}, | ||
method: 'GET', | ||
uri: `${credentials.host}/rest/v1/`, | ||
json: true, | ||
}; | ||
|
||
return this.helpers.request!(options); | ||
} |
Oops, something went wrong.