From cbc1b7f2a387d1f3c61cbf6e391947cad3fb4076 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Thu, 19 Nov 2020 03:04:20 -0500 Subject: [PATCH] :sparkles: Add Cloud Natural Language node (#1181) * :sparkles: Add Cloud Natural Language node * :arrow_up: Set google-fonts-webpack-plugin to latest version * :bug: Rename method composite to compose * :zap: Improvements to Google Cloud Natural Language Node Improvements to #1171 Co-authored-by: Tanay Pant --- ...oudNaturalLanguageOAuth2Api.credentials.ts | 26 ++ packages/nodes-base/nodes/EditImage.node.ts | 2 +- .../CloudNaturalLanguage/GenericFunctions.ts | 59 +++ .../GoogleCloudNaturalLanguage.node.ts | 337 ++++++++++++++++++ .../Google/CloudNaturalLanguage/Interface.ts | 11 + .../googleCloudnaturallanguage.png | Bin 0 -> 541 bytes packages/nodes-base/package.json | 2 + 7 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 packages/nodes-base/credentials/GoogleCloudNaturalLanguageOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts create mode 100644 packages/nodes-base/nodes/Google/CloudNaturalLanguage/Interface.ts create mode 100644 packages/nodes-base/nodes/Google/CloudNaturalLanguage/googleCloudnaturallanguage.png diff --git a/packages/nodes-base/credentials/GoogleCloudNaturalLanguageOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleCloudNaturalLanguageOAuth2Api.credentials.ts new file mode 100644 index 0000000000..25cd0faa70 --- /dev/null +++ b/packages/nodes-base/credentials/GoogleCloudNaturalLanguageOAuth2Api.credentials.ts @@ -0,0 +1,26 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +const scopes = [ + 'https://www.googleapis.com/auth/cloud-language', + 'https://www.googleapis.com/auth/cloud-platform', +]; + +export class GoogleCloudNaturalLanguageOAuth2Api implements ICredentialType { + name = 'googleCloudNaturalLanguageOAuth2Api'; + extends = [ + 'googleOAuth2Api', + ]; + displayName = 'Google Cloud Natural Language OAuth2 API'; + documentationUrl = 'google'; + properties = [ + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' '), + }, + ]; +} diff --git a/packages/nodes-base/nodes/EditImage.node.ts b/packages/nodes-base/nodes/EditImage.node.ts index 65e4bef23e..382fa4097a 100644 --- a/packages/nodes-base/nodes/EditImage.node.ts +++ b/packages/nodes-base/nodes/EditImage.node.ts @@ -861,7 +861,7 @@ export class EditImage implements INodeType { cleanupFunctions.push(cleanup); fsWriteFileAsync(fd, Buffer.from(item.binary![dataPropertyNameComposite as string].data, BINARY_ENCODING)); - gmInstance = gmInstance.composite(path).geometry(geometryString); + gmInstance = gmInstance.compose(path).geometry(geometryString); } else if (operation === 'crop') { const width = this.getNodeParameter('width') as number; const height = this.getNodeParameter('height') as number; diff --git a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts new file mode 100644 index 0000000000..02d521c9c4 --- /dev/null +++ b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts @@ -0,0 +1,59 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, + endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + let options: OptionsWithUri = { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://language.googleapis.com${endpoint}`, + json: true, + }; + + options = Object.assign({}, options, option); + + try { + if (Object.keys(body).length === 0) { + delete options.body; + } + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'googleCloudNaturalLanguageOAuth2Api', options); + + } catch (error) { + if (error.response && error.response.body && error.response.body.error) { + + let errorMessages; + + if (error.response.body.error.errors) { + // Try to return the error prettier + errorMessages = error.response.body.error.errors; + + errorMessages = errorMessages.map((errorItem: IDataObject) => errorItem.message); + + errorMessages = errorMessages.join('|'); + + } else if (error.response.body.error.message) { + errorMessages = error.response.body.error.message; + } + + throw new Error(`Google Cloud Natural Language error response [${error.statusCode}]: ${errorMessages}`); + } + throw error; + } +} diff --git a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts new file mode 100644 index 0000000000..a490c06e02 --- /dev/null +++ b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts @@ -0,0 +1,337 @@ + +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + IData, +} from './Interface'; + +import { + googleApiRequest, +} from './GenericFunctions'; + +export class GoogleCloudNaturalLanguage implements INodeType { + description: INodeTypeDescription = { + displayName: 'Google Cloud Natural Language', + name: 'googleCloudNaturalLanguage', + icon: 'file:googleCloudnaturallanguage.png', + group: ['input', 'output'], + version: 1, + description: 'Consume Google Cloud Natural Language API', + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + defaults: { + name: 'Google Cloud Natural Language', + color: '#5288f0', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'googleCloudNaturalLanguageOAuth2Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Document', + value: 'document', + }, + ], + default: 'document', + description: 'The resource to operate on.', + }, + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'document', + ], + }, + }, + options: [ + { + name: 'Analyze Sentiment', + value: 'analyzeSentiment', + description: 'Analyze Sentiment', + }, + ], + default: 'analyzeSentiment', + description: 'The operation to perform', + }, + // ---------------------------------- + // All + // ---------------------------------- + { + displayName: 'Document Type', + name: 'documentType', + type: 'options', + options: [ + { + name: 'Unspecified', + value: 'TYPE_UNSPECIFIED', + }, + { + name: 'Plain Text', + value: 'PLAIN_TEXT', + }, + { + name: 'HTML', + value: 'HTML', + }, + ], + default: '', + description: 'The type of input document.', + required: true, + displayOptions: { + show: { + operation: [ + 'analyzeSentiment', + ], + }, + }, + }, + { + displayName: 'Source', + name: 'source', + type: 'options', + options: [ + { + name: 'Content', + value: 'content', + }, + { + name: 'Google Cloud Storage URI', + value: 'gcsContentUri', + }, + ], + default: 'content', + description: 'The source of the document: a string containing the content or a Google Cloud Storage URI.', + required: true, + displayOptions: { + show: { + operation: [ + 'analyzeSentiment', + ], + }, + }, + }, + { + displayName: 'Content', + name: 'content', + type: 'string', + default: '', + description: 'The content of the input in string format. Cloud audit logging exempt since it is based on user data. ', + required: true, + displayOptions: { + show: { + operation: [ + 'analyzeSentiment', + ], + source: [ + 'content', + ], + }, + }, + }, + { + displayName: 'Google Cloud Storage URI', + name: 'gcsContentUri', + type: 'string', + default: '', + description: 'The Google Cloud Storage URI where the file content is located. This URI must be of the form: gs://bucket_name/object_name.
For more details, see https://cloud.google.com/storage/docs/reference-uris.', + required: true, + displayOptions: { + show: { + operation: [ + 'analyzeSentiment', + ], + source: [ + 'gcsContentUri', + ], + }, + }, + }, + { + displayName: 'Encoding Type', + name: 'encodingType', + type: 'options', + options: [ + { + name: 'None', + value: 'NONE', + }, + { + name: 'UTF-8', + value: 'UTF8', + }, + { + name: 'UTF-16', + value: 'UTF16', + }, + { + name: 'UTF-32', + value: 'UTF32', + }, + ], + default: '', + description: 'The encoding type used by the API to calculate sentence offsets.', + required: true, + displayOptions: { + show: { + operation: [ + 'analyzeSentiment', + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'analyzeSentiment', + ], + }, + }, + default: {}, + description: '', + placeholder: 'Add Option', + options: [ + { + displayName: 'Language', + name: 'language', + type: 'options', + options: [ + { + name: 'Arabic', + value: 'ar', + }, + { + name: 'Chinese (Simplified) ', + value: 'zh', + }, + { + name: 'Chinese (Traditional)', + value: 'zh-Hant', + }, + { + name: 'Dutch', + value: 'nl', + }, + { + name: 'English', + value: 'en', + }, + { + name: 'French', + value: 'fr', + }, + { + name: 'German', + value: 'de', + }, + { + name: 'Indonesian', + value: 'id', + }, + { + name: 'Italian', + value: 'it', + }, + { + name: 'Japanese', + value: 'ja', + }, + { + name: 'Korean', + value: 'ko', + }, + { + name: 'Portuguese (Brazilian & Continental)', + value: 'pt', + }, + { + name: 'Spanish', + value: 'es', + }, + { + name: 'Thai', + value: 'th', + }, + { + name: 'Turkish', + value: 'tr', + }, + { + name: 'Vietnamese', + value: 'vi', + }, + ], + default: 'en', + placeholder: '', + description: 'The language of the document (if not specified, the language is automatically detected). Both ISO and BCP-47 language codes are accepted.', + }, + ], + }, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const length = items.length as unknown as number; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + const responseData = []; + for (let i = 0; i < length; i++) { + if (resource === 'document') { + if (operation === 'analyzeSentiment') { + const source = this.getNodeParameter('source', i) as string; + const documentType = this.getNodeParameter('documentType', i) as string; + const encodingType = this.getNodeParameter('encodingType', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + const body: IData = { + document: { + type: documentType, + }, + encodingType, + }; + + if (source === 'content') { + const content = this.getNodeParameter('content', i) as string; + body.document.content = content; + } else { + const gcsContentUri = this.getNodeParameter('gcsContentUri', i) as string; + body.document.gcsContentUri = gcsContentUri; + } + + if (options.language) { + body.document.language = options.language as string; + } + + const response = await googleApiRequest.call(this, 'POST', `/v1/documents:analyzeSentiment`, body); + responseData.push(response); + } + } + } + return [this.helpers.returnJsonArray(responseData)]; + } +} diff --git a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/Interface.ts b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/Interface.ts new file mode 100644 index 0000000000..bdbc494b2d --- /dev/null +++ b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/Interface.ts @@ -0,0 +1,11 @@ +export interface IData { + document: IDocument; + encodingType: string; +} + +export interface IDocument { + type: string; + language?: string; + content?: string; + gcsContentUri?: string; +} \ No newline at end of file diff --git a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/googleCloudnaturallanguage.png b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/googleCloudnaturallanguage.png new file mode 100644 index 0000000000000000000000000000000000000000..06a067e4eae20a7c96b00a34a1ba9ce9e7ee9a7b GIT binary patch literal 541 zcmV+&0^*_qjy zZFXR8_0~Y((X=$_+#bw*RS>;ku;+1j;j0CO<;ooFo9S8x-$ z_uy7E1?xNbFWNa6JDlKu=n%klbTD9(&BFp$Ef31}@odEg-Ul=*)&{JB9^Sn^ zv4ed^JHgt5#n88J*UrE`qMc$fV3+9g`;QanU<}%%^ZjfW3y$-mjBZ}`6H7ufVTZ~4 fsW=!5*KYO~{OWGIt*u|600000NkvXXu0mjfus{Q% literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 8e76b9045d..1ce157c0b9 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -82,6 +82,7 @@ "dist/credentials/GoogleBooksOAuth2Api.credentials.js", "dist/credentials/GoogleCalendarOAuth2Api.credentials.js", "dist/credentials/GoogleContactsOAuth2Api.credentials.js", + "dist/credentials/GoogleCloudNaturalLanguageOAuth2Api.credentials.js", "dist/credentials/GoogleDriveOAuth2Api.credentials.js", "dist/credentials/GoogleFirebaseCloudFirestoreOAuth2Api.credentials.js", "dist/credentials/GoogleFirebaseRealtimeDatabaseOAuth2Api.credentials.js", @@ -291,6 +292,7 @@ "dist/nodes/Gitlab/GitlabTrigger.node.js", "dist/nodes/Google/Books/GoogleBooks.node.js", "dist/nodes/Google/Calendar/GoogleCalendar.node.js", + "dist/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.js", "dist/nodes/Google/Contacts/GoogleContacts.node.js", "dist/nodes/Google/Drive/GoogleDrive.node.js", "dist/nodes/Google/Firebase/CloudFirestore/CloudFirestore.node.js",