- First 'working' version of the Zapier plugin. Needs further debugging and needs additional functionalty (only add_document.js)
This commit is contained in:
4
integrations/Zapier/eveai_integration/.zapierapprc
Normal file
4
integrations/Zapier/eveai_integration/.zapierapprc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"id": 216725,
|
||||
"key": "App216725"
|
||||
}
|
||||
24
integrations/Zapier/eveai_integration/README.md
Normal file
24
integrations/Zapier/eveai_integration/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# eveai_integration
|
||||
|
||||
This Zapier integration project is generated by the `zapier init` CLI command.
|
||||
|
||||
These are what you normally do next:
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install # or you can use yarn
|
||||
|
||||
# Run tests
|
||||
zapier test
|
||||
|
||||
# Register the integration on Zapier if you haven't
|
||||
zapier register "App Title"
|
||||
|
||||
# Or you can link to an existing integration on Zapier
|
||||
zapier link
|
||||
|
||||
# Push it to Zapier
|
||||
zapier push
|
||||
```
|
||||
|
||||
Find out more on the latest docs: https://github.com/zapier/zapier-platform/blob/main/packages/cli/README.md.
|
||||
93
integrations/Zapier/eveai_integration/api_client.js
Normal file
93
integrations/Zapier/eveai_integration/api_client.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// api_client.js
|
||||
const BASE_URL = 'https://evie.askeveai.com/api/api/v1';
|
||||
|
||||
class EveAIApiClient {
|
||||
constructor(z, bundle) {
|
||||
this.z = z;
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
async ensure_valid_token() {
|
||||
const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
|
||||
const token = this.bundle.authData.access_token;
|
||||
const tokenExpiry = this.bundle.authData.token_expiry;
|
||||
// Check if token is expired or will expire in next 30 seconds
|
||||
if (!token || !tokenExpiry || currentTime + 30 >= tokenExpiry) {
|
||||
this.z.console.log('Token missing or expiring soon, requesting new token...');
|
||||
|
||||
const response = await this.z.request({
|
||||
url: `${BASE_URL}/auth/token`,
|
||||
method: 'POST',
|
||||
body: {
|
||||
tenant_id: this.bundle.authData.tenant_id,
|
||||
api_key: this.bundle.authData.api_key,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Failed to get access token: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = response.json;
|
||||
|
||||
// Update the bundle's authData
|
||||
this.bundle.authData.access_token = data.access_token;
|
||||
this.bundle.authData.token_expiry = currentTime + data.expires_in;
|
||||
|
||||
this.z.console.log('New token obtained:', {
|
||||
token_prefix: data.access_token.substring(0, 10) + '...',
|
||||
expires_in: data.expires_in,
|
||||
expiry_time: this.bundle.authData.token_expiry
|
||||
});
|
||||
|
||||
return data.access_token;
|
||||
}
|
||||
|
||||
this.z.console.log('Using existing valid token');
|
||||
return token;
|
||||
}
|
||||
|
||||
async make_request(method, endpoint, data = null) {
|
||||
try {
|
||||
// Ensure we have a valid token
|
||||
const token = await this.ensure_valid_token();
|
||||
|
||||
this.z.console.log('Making request:', {
|
||||
method,
|
||||
endpoint,
|
||||
token_prefix: token.substring(0, 10) + '...'
|
||||
});
|
||||
|
||||
const requestConfig = {
|
||||
url: `${BASE_URL}${endpoint}`,
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
if (data) {
|
||||
requestConfig.body = data;
|
||||
}
|
||||
|
||||
const response = await this.z.request(requestConfig);
|
||||
|
||||
this.z.console.log('Response received:', {
|
||||
status: response.status,
|
||||
data: response.json
|
||||
});
|
||||
|
||||
return response.json;
|
||||
|
||||
} catch (error) {
|
||||
this.z.console.error('Request failed:', {
|
||||
error: error.message,
|
||||
response: error.response
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EveAIApiClient;
|
||||
140
integrations/Zapier/eveai_integration/authentication.js
Normal file
140
integrations/Zapier/eveai_integration/authentication.js
Normal file
@@ -0,0 +1,140 @@
|
||||
const EveAIApiClient = require('./api_client');
|
||||
const handleError = (z, error) => {
|
||||
// Log the full error for debugging
|
||||
z.console.error('Authentication error:', {
|
||||
message: error.message,
|
||||
response: error.response ? {
|
||||
status: error.response.status,
|
||||
data: error.response.data,
|
||||
headers: error.response.headers
|
||||
} : 'No response',
|
||||
stack: error.stack
|
||||
});
|
||||
|
||||
// If we have a response with a message from the API, use it
|
||||
if (error.response) {
|
||||
if (error.response.status) {
|
||||
switch (error.response.status) {
|
||||
case 400:
|
||||
throw new Error('Invalid request. Please verify your Tenant ID and API Key format.');
|
||||
case 401:
|
||||
throw new Error('Authentication failed. Please verify your credentials and ensure your API Key is active.');
|
||||
case 403:
|
||||
throw new Error('Access forbidden. Your API Key may not have the required permissions.');
|
||||
case 404:
|
||||
throw new Error('Authentication service not found. Please verify the API URL.');
|
||||
case 429:
|
||||
throw new Error('Too many authentication attempts. Please try again later.');
|
||||
case 500:
|
||||
throw new Error('EveAI server encountered an error. Please try again later.');
|
||||
default:
|
||||
throw new Error(`Unexpected error (${error.response.status}). Please contact support if this persists.`);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have error data but no status, try to use the error message
|
||||
if (error.response.data) {
|
||||
if (error.response.data.message) {
|
||||
throw new Error(`API Error: ${error.response.data.message}`);
|
||||
}
|
||||
if (typeof error.response.data === 'string') {
|
||||
throw new Error(`API Error: ${error.response.data}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle network errors
|
||||
if (error.message.includes('ECONNREFUSED')) {
|
||||
throw new Error('Unable to connect to EveAI. Please check your network connection.');
|
||||
}
|
||||
|
||||
if (error.message.includes('ETIMEDOUT')) {
|
||||
throw new Error('Connection to EveAI timed out. Please try again.');
|
||||
}
|
||||
|
||||
// Generic error for unhandled cases
|
||||
throw new Error(`Authentication failed: ${error.message}`);
|
||||
};
|
||||
|
||||
const testAuth = async (z, bundle) => {
|
||||
try {
|
||||
const client = new EveAIApiClient(z, bundle);
|
||||
await client.ensure_valid_token();
|
||||
|
||||
// Make a test request to verify the token works
|
||||
const response = await client.make_request('GET', '/auth/verify');
|
||||
|
||||
// Log successful authentication
|
||||
z.console.log('Authentication successful', {
|
||||
tenant_id: bundle.authData.tenant_id,
|
||||
token_prefix: bundle.authData.access_token.substring(0, 10) + '...',
|
||||
expiry: new Date(bundle.authData.token_expiry * 1000).toISOString()
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
// Use our enhanced error handler
|
||||
handleError(z, error);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
type: 'session',
|
||||
fields: [
|
||||
{
|
||||
helpText:
|
||||
"The tenant_id provided to you by email, or to be created / retrieved in Evie's administrative interface (https://evie.askeveai.com/admin)",
|
||||
computed: false,
|
||||
key: 'tenant_id',
|
||||
required: true,
|
||||
label: 'Tenant ID',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
helpText:
|
||||
"The api_key provided to you by email, or to be created / retrieved in Evie's administrative interface (https://evie.askeveai.com/admin)",
|
||||
computed: false,
|
||||
key: 'api_key',
|
||||
required: true,
|
||||
label: 'API Key',
|
||||
type: 'password',
|
||||
},
|
||||
],
|
||||
sessionConfig: {
|
||||
perform: async (z, bundle) => {
|
||||
try {
|
||||
const client = new EveAIApiClient(z, bundle);
|
||||
const token = await client.ensure_valid_token();
|
||||
|
||||
z.console.log('Session token obtained', {
|
||||
token_prefix: token.substring(0, 10) + '...',
|
||||
expiry: new Date(bundle.authData.token_expiry * 1000).toISOString()
|
||||
});
|
||||
|
||||
return {
|
||||
access_token: token,
|
||||
token_expiry: bundle.authData.token_expiry
|
||||
};
|
||||
} catch (error) {
|
||||
handleError(z, error);
|
||||
}
|
||||
}
|
||||
},
|
||||
test: testAuth,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
262
integrations/Zapier/eveai_integration/creates/add_document.js
Normal file
262
integrations/Zapier/eveai_integration/creates/add_document.js
Normal file
@@ -0,0 +1,262 @@
|
||||
const EveAIApiClient = require('../api_client');
|
||||
|
||||
module.exports = {
|
||||
display: {
|
||||
description: "This action uploads a new document to Evie's Library",
|
||||
hidden: false,
|
||||
label: 'Add a new document to Evie',
|
||||
},
|
||||
key: 'add_document',
|
||||
noun: 'Document',
|
||||
operation: {
|
||||
inputFields: [
|
||||
{
|
||||
key: 'catalog_id',
|
||||
label: 'Catalog ID',
|
||||
type: 'integer',
|
||||
helpText:
|
||||
"The ID of the Catalog in Evie's Library you want to add the document to.",
|
||||
required: true,
|
||||
list: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
{
|
||||
key: 'file',
|
||||
label: 'The File Content',
|
||||
type: 'file',
|
||||
helpText:
|
||||
"The content of the file that needs to uploaded and indexed in Evie's Library",
|
||||
required: true,
|
||||
list: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Document Name',
|
||||
type: 'string',
|
||||
helpText: 'The name you want to give the Document.',
|
||||
required: false,
|
||||
list: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
{
|
||||
key: 'language',
|
||||
label: 'Document Language',
|
||||
type: 'string',
|
||||
default: 'en',
|
||||
helpText: 'Two-letter-code of the language the document is written in.',
|
||||
required: true,
|
||||
list: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
{
|
||||
key: 'user_context',
|
||||
label: 'User Context',
|
||||
type: 'text',
|
||||
helpText:
|
||||
'Contextual information you want to add to the Document. If you have structured information to be shared, you can better add this information to the User Metadata, which allows for json to be uploaded.',
|
||||
required: false,
|
||||
list: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
{
|
||||
key: 'valid_from',
|
||||
label: 'Valid From',
|
||||
type: 'datetime',
|
||||
helpText:
|
||||
'The moment this document is valid. When no value is given, the current data will be used.',
|
||||
required: false,
|
||||
list: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
{
|
||||
key: 'metadata_service',
|
||||
label: 'Service',
|
||||
type: 'string',
|
||||
default: 'Zapier',
|
||||
helpText: "By default we use 'Zapier' as service name. However, if you need to change that to e.g. give an indication of the Zapier flow, you can change this value.",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'metadata_source',
|
||||
label: 'Source App',
|
||||
type: 'string',
|
||||
helpText: "The source app of the document's origin. e.g. 'Dropbox' if the document is provided through Dropbox, or 'Google Docs' if that happens to be the origin of the document.",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
key: 'metadata_unique_id',
|
||||
label: 'Unique ID',
|
||||
type: 'string',
|
||||
helpText: 'An unique identifier, provided by the source system, if that is available.',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
key: 'metadata_unique_url',
|
||||
label: 'Unique URL',
|
||||
type: 'string',
|
||||
helpText: "A unique URL that is provided by the source system, if that's available",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
key: 'additional_metadata',
|
||||
label: 'Additional Metadata',
|
||||
helpText: "Extra metadata you'd like to add to the document",
|
||||
dict: true,
|
||||
required: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
{
|
||||
key: 'catalog_properties',
|
||||
label: 'Catalog Properties',
|
||||
helpText:
|
||||
'Depending on the Catalog ID provided, you can add the required key-value pairs here.',
|
||||
dict: true,
|
||||
required: false,
|
||||
altersDynamicFields: false,
|
||||
},
|
||||
],
|
||||
perform: async (z, bundle) => {
|
||||
try {
|
||||
z.console.log("Starting New Log Trace for add_document");
|
||||
z.console.log("=======================================");
|
||||
|
||||
const client = new EveAIApiClient(z, bundle);
|
||||
|
||||
// Prepare base metadata
|
||||
const baseMetadata = {
|
||||
service: bundle.inputData.metadata_service || 'Zapier',
|
||||
source: bundle.inputData.metadata_source || '',
|
||||
unique_id: bundle.inputData.metadata_unique_id || '',
|
||||
unique_url: bundle.inputData.metadata_unique_url || '',
|
||||
};
|
||||
|
||||
// If there's additional metadata, merge it
|
||||
if (bundle.inputData.additional_metadata) {
|
||||
Object.assign(baseMetadata, bundle.inputData.additional_metadata);
|
||||
}
|
||||
|
||||
// Get the file content
|
||||
const filePromise = z.stashFile(bundle.inputData.file);
|
||||
const file = await filePromise;
|
||||
// const temp_url = z.stashFile(bundle.inputData.file);
|
||||
|
||||
// Create request data as an object
|
||||
const requestData = {
|
||||
catalog_id: bundle.inputData.catalog_id,
|
||||
language: bundle.inputData.language,
|
||||
temp_url: file, // This will be handled by z.request automatically
|
||||
user_metadata: JSON.stringify(baseMetadata),
|
||||
};
|
||||
|
||||
// Add name property if it exists
|
||||
if (bundle.inputData.name) {
|
||||
requestData.name = bundle.inputData.name;
|
||||
}
|
||||
|
||||
// Add user_context property if it exists
|
||||
if (bundle.inputData.user_context) {
|
||||
requestData.user_context = bundle.inputData.user_context;
|
||||
}
|
||||
|
||||
// Add valid_from property if it exists
|
||||
if (bundle.inputData.valid_from) {
|
||||
requestData.valid_from = bundle.inputData.valid_from;
|
||||
}
|
||||
|
||||
// Add catalog properties if they exist
|
||||
if (bundle.inputData.catalog_properties) {
|
||||
requestData.catalog_properties = JSON.stringify(bundle.inputData.catalog_properties);
|
||||
}
|
||||
|
||||
// Make request to API
|
||||
return await client.make_request('POST', '/documents/add_document_through_url', requestData);
|
||||
|
||||
} catch (error) {
|
||||
// Enhanced error logging
|
||||
z.console.error('Error details:', {
|
||||
message: error.message,
|
||||
response: error.response ? {
|
||||
status: error.response.status,
|
||||
headers: error.response.headers,
|
||||
data: error.response.data
|
||||
} : 'No response',
|
||||
request: error.request ? {
|
||||
method: error.request.method,
|
||||
url: error.request.url,
|
||||
headers: error.request.headers
|
||||
} : 'No request'
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// perform: async (z, bundle) => {
|
||||
// try {
|
||||
// z.console.log("Starting New Log Trace for add_document")
|
||||
// z.console.log("=======================================")
|
||||
//
|
||||
// // Prepare base metadata
|
||||
// const baseMetadata = {
|
||||
// service: bundle.inputData.metadata_service || 'Zapier',
|
||||
// source: bundle.inputData.metadata_source,
|
||||
// unique_id: bundle.inputData.metadata_unique_id,
|
||||
// unique_url: bundle.inputData.metadata_unique_url,
|
||||
// };
|
||||
//
|
||||
// // If there's additional metadata, merge it with the base metadata
|
||||
// if (bundle.inputData.additional_metadata) {
|
||||
// Object.assign(baseMetadata, bundle.inputData.additional_metadata);
|
||||
// }
|
||||
//
|
||||
// const requestData = {
|
||||
//
|
||||
// catalog_id: bundle.inputData.catalog_id,
|
||||
// language: bundle.inputData.language,
|
||||
//
|
||||
// // Add optional fields if they exist
|
||||
// name: bundle.inputData.name || undefined,
|
||||
// user_context: bundle.inputData.user_context || undefined,
|
||||
// valid_from: bundle.inputData.valid_from || undefined,
|
||||
// user_metadata: JSON.stringify(baseMetadata),
|
||||
// catalog_properties: JSON.stringify(bundle.inputData.catalog_properties) || undefined,
|
||||
// file: z.stashFile(bundle.inputData.file),
|
||||
// }
|
||||
//
|
||||
// // Make request to your API
|
||||
// const response = await z.request({
|
||||
// url: 'https://evie.askeveai.com/api/api/v1/documents/add_document',
|
||||
// method: 'POST',
|
||||
// body: requestData,
|
||||
// headers: {
|
||||
// 'Authorization': `Bearer ${bundle.authData.access_token}`,
|
||||
// 'Content-Type': 'multipart/form-data',
|
||||
// },
|
||||
// });
|
||||
//
|
||||
// // Log the response for debugging
|
||||
// z.console.log('API Response:', {
|
||||
// status: response.status,
|
||||
// body: response.data
|
||||
// });
|
||||
// // Return the parsed response
|
||||
// return response.json;
|
||||
// } catch (error) {
|
||||
// // Enhanced error logging
|
||||
// z.console.error('Error details:', {
|
||||
// message: error.message,
|
||||
// response: error.response ? {
|
||||
// status: error.response.status,
|
||||
// headers: error.response.headers,
|
||||
// data: error.response.data
|
||||
// } : 'No response',
|
||||
// request: error.request ? {
|
||||
// method: error.request.method,
|
||||
// url: error.request.url,
|
||||
// headers: error.request.headers
|
||||
// } : 'No request'
|
||||
// });
|
||||
// throw error;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
};
|
||||
25
integrations/Zapier/eveai_integration/index.js
Normal file
25
integrations/Zapier/eveai_integration/index.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const authentication = require('./authentication');
|
||||
const addDocument = require('./creates/add_document');
|
||||
|
||||
module.exports = {
|
||||
// This is just shorthand to reference the installed dependencies you have.
|
||||
// Zapier will need to know these before we can upload.
|
||||
version: require('./package.json').version,
|
||||
platformVersion: require('zapier-platform-core').version,
|
||||
|
||||
// Register the authentication
|
||||
authentication: authentication,
|
||||
|
||||
// If you want your trigger to show up, you better include it here!
|
||||
triggers: {},
|
||||
|
||||
// If you want your searches to show up, you better include it here!
|
||||
searches: {},
|
||||
|
||||
// If you want your creates to show up, you better include it here!
|
||||
creates: {
|
||||
[addDocument.key]: addDocument
|
||||
},
|
||||
|
||||
resources: {},
|
||||
};
|
||||
4040
integrations/Zapier/eveai_integration/package-lock.json
generated
Normal file
4040
integrations/Zapier/eveai_integration/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
integrations/Zapier/eveai_integration/package.json
Normal file
16
integrations/Zapier/eveai_integration/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "eveai_integration",
|
||||
"version": "1.0.3",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "jest --testTimeout 10000"
|
||||
},
|
||||
"dependencies": {
|
||||
"zapier-platform-core": "15.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.6.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
Reference in New Issue
Block a user