- First 'working' version of the Zapier plugin. Needs further debugging and needs additional functionalty (only add_document.js)

This commit is contained in:
Josako
2024-12-12 16:36:41 +01:00
parent d35ec9f5ae
commit 46c60b36a0
14 changed files with 4875 additions and 17 deletions

View File

@@ -1,6 +1,7 @@
from flask import request, session
import time
from flask_security import current_user
import json
def log_request_middleware(app):
@@ -59,3 +60,97 @@ def log_request_middleware(app):
@app.after_request
def log_session_state_after(response):
return response
def register_request_debugger(app):
@app.before_request
def debug_request_info():
"""Log consolidated request information for debugging"""
# Skip health check endpoints
if request.path.startswith('/_healthz') or request.path.startswith('/healthz'):
return
# Gather all request information in a structured way
debug_info = {
"basic_info": {
"method": request.method,
"path": request.path,
"content_type": request.content_type,
"content_length": request.content_length
},
"environment": {
"remote_addr": request.remote_addr,
"user_agent": str(request.user_agent)
}
}
# Add headers (excluding sensitive ones)
safe_headers = {k: v for k, v in request.headers.items()
if k.lower() not in ('authorization', 'cookie', 'x-api-key')}
debug_info["headers"] = safe_headers
# Add authentication info (presence only)
auth_header = request.headers.get('Authorization', '')
debug_info["auth_info"] = {
"has_auth_header": bool(auth_header),
"auth_type": auth_header.split(' ')[0] if auth_header else None,
"token_length": len(auth_header.split(' ')[1]) if auth_header and len(auth_header.split(' ')) > 1 else 0,
"header_format": 'Valid format' if auth_header.startswith('Bearer ') else 'Invalid format',
"raw_header": auth_header[:10] + '...' if auth_header else None # Show first 10 chars only
}
# Add request data based on type
if request.is_json:
try:
json_data = request.get_json()
if isinstance(json_data, dict):
# Remove sensitive fields from logging
safe_json = {k: v for k, v in json_data.items()
if not any(sensitive in k.lower()
for sensitive in ['password', 'token', 'secret', 'key'])}
debug_info["request_data"] = {
"type": "json",
"content": safe_json
}
except Exception as e:
debug_info["request_data"] = {
"type": "json",
"error": str(e)
}
elif request.form:
safe_form = {k: v for k, v in request.form.items()
if not any(sensitive in k.lower()
for sensitive in ['password', 'token', 'secret', 'key'])}
debug_info["request_data"] = {
"type": "form",
"content": safe_form
}
# Add file information if present
if request.files:
debug_info["files"] = {
name: {
"filename": f.filename,
"content_type": f.content_type,
"content_length": f.content_length if hasattr(f, 'content_length') else None
}
for name, f in request.files.items()
}
# Add CORS information if present
cors_headers = {
"origin": request.headers.get('Origin'),
"request_method": request.headers.get('Access-Control-Request-Method'),
"request_headers": request.headers.get('Access-Control-Request-Headers')
}
if any(cors_headers.values()):
debug_info["cors"] = {k: v for k, v in cors_headers.items() if v is not None}
# Format the debug info as a pretty-printed JSON string with indentation
formatted_debug_info = json.dumps(debug_info, indent=2, sort_keys=True)
# Log everything in a single statement
app.logger.debug(
"Request Debug Information\n",
extra={"request_debug\n": formatted_debug_info}
)

View File

@@ -18,6 +18,7 @@ from .api.auth import auth_ns
from config.config import get_config
from common.utils.celery_utils import make_celery, init_celery
from common.utils.eveai_exceptions import EveAIException
from common.utils.debug_utils import register_request_debugger
def create_app(config_file=None):
@@ -55,6 +56,9 @@ def create_app(config_file=None):
# Register Error Handlers
register_error_handlers(app)
# Register Request Debugger
register_request_debugger(app)
@app.before_request
def check_cors():
if request.method == 'OPTIONS':

View File

@@ -1,10 +1,11 @@
from datetime import timedelta, datetime as dt, timezone as tz
from flask_restx import Namespace, Resource, fields
from flask_jwt_extended import create_access_token, verify_jwt_in_request, get_jwt
from flask_jwt_extended import create_access_token, verify_jwt_in_request, get_jwt, get_jwt_identity, jwt_required
from common.models.user import Tenant, TenantProject
from common.extensions import simple_encryption
from flask import current_app, request
from flask import current_app, jsonify, request
from functools import wraps
auth_ns = Namespace('auth', description='Authentication related operations')
@@ -36,7 +37,6 @@ class Token(Resource):
"""
Get JWT token
"""
current_app.logger.debug(f'Token Requested {auth_ns.payload}')
try:
tenant_id = int(auth_ns.payload['tenant_id'])
api_key = auth_ns.payload['api_key']
@@ -71,16 +71,19 @@ class Token(Resource):
current_app.logger.error(f"Project for given API key not found for Tenant: {tenant_id}")
return {'message': "Invalid API key"}, 401
if "DOCAPI" not in matching_project.services:
current_app.logger.error(f"Service DOCAPI not authorized for Project {matching_project.name} "
f"for Tenant: {tenant_id}")
return {'message': f"Service DOCAPI not authorized for Project {matching_project.name}"}, 403
# Get the JWT_ACCESS_TOKEN_EXPIRES setting from the app config
expires_delta = current_app.config.get('JWT_ACCESS_TOKEN_EXPIRES', timedelta(minutes=15))
try:
access_token = create_access_token(identity=tenant_id, expires_delta=expires_delta)
additional_claims = {
'services': matching_project.services,
}
access_token = create_access_token(
identity=tenant_id,
expires_delta=expires_delta,
additional_claims=additional_claims
)
current_app.logger.debug(f"Created token: {access_token}")
return {
'access_token': access_token,
'expires_in': expires_delta.total_seconds()
@@ -146,3 +149,50 @@ class TokenRefresh(Resource):
except Exception as e:
current_app.logger.error(f"Token refresh failed: {str(e)}")
return {'message': 'Token refresh failed'}, 401
@auth_ns.route('/services')
class Services(Resource):
@jwt_required()
@auth_ns.doc(security='Bearer')
@auth_ns.response(200, 'Success', {
'services': fields.List(fields.String, description='List of allowed services for this token'),
'tenant_id': fields.Integer(description='Tenant ID associated with this token')
})
@auth_ns.response(401, 'Invalid or expired token')
def get(self):
"""
Get allowed services for the current token
"""
# Log the incoming authorization header
auth_header = request.headers.get('Authorization')
current_app.logger.debug(f"Received Authorization header: {auth_header}")
claims = get_jwt()
tenant_id = get_jwt_identity()
return {
'services': claims.get('services', []),
'tenant_id': tenant_id
}, 200
# Decorate function to check for a particular service
def requires_service(service_name):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
# Get the JWT claims
claims = get_jwt()
services = claims.get('services', [])
if service_name not in services:
return {
'message': f'This endpoint requires the {service_name} service',
'error': 'Insufficient permissions'
}, 403
return fn(*args, **kwargs)
return wrapper
return decorator

View File

@@ -1,7 +1,9 @@
import io
import json
from datetime import datetime
import pytz
import requests
from flask import current_app, request
from flask_restx import Namespace, Resource, fields, reqparse
from flask_jwt_extended import jwt_required, get_jwt_identity
@@ -9,9 +11,9 @@ from werkzeug.datastructures import FileStorage
from werkzeug.utils import secure_filename
from common.utils.document_utils import (
create_document_stack, process_url, start_embedding_task,
validate_file_type, EveAIInvalidLanguageException, EveAIDoubleURLException, EveAIUnsupportedFileType,
EveAIInvalidLanguageException, EveAIDoubleURLException, EveAIUnsupportedFileType,
get_documents_list, edit_document, refresh_document, edit_document_version,
refresh_document_with_info, lookup_document
refresh_document_with_info, lookup_document, refresh_document_with_content
)
from common.utils.eveai_exceptions import EveAIException
from eveai_api.api.auth import requires_service
@@ -74,12 +76,17 @@ class AddDocument(Resource):
try:
args = upload_parser.parse_args()
except Exception as e:
current_app.logger.error(f"Error parsing arguments: {str(e)}")
current_app.logger.error(f"Exception type: {type(e)}")
raise
try:
file = args['file']
filename = secure_filename(file.filename)
extension = filename.rsplit('.', 1)[1].lower()
validate_file_type(extension)
# validate_file_type(extension)
api_input = {
'catalog_id': args.get('catalog_id'),
@@ -109,6 +116,105 @@ class AddDocument(Resource):
document_ns.abort(500, 'Error adding document')
# Models for AddDocumentThroughURL
add_document_through_url = document_ns.model('AddDocumentThroughURL', {
'catalog_id': fields.Integer(required=True, description='ID of the catalog the URL needs to be added to'),
'temp_url': fields.String(required=True, description='Temporary URL of the document to add'),
'name': fields.String(required=False, description='Name of the document'),
'language': fields.String(required=True, description='Language of the document'),
'user_context': fields.String(required=False, description='User context for the document'),
'valid_from': fields.String(required=False, description='Valid from date for the document'),
'user_metadata': fields.String(required=False, description='User metadata for the document'),
'system_metadata': fields.String(required=False, description='System metadata for the document'),
'catalog_properties': fields.String(required=False, description='The catalog configuration to be passed along (JSON '
'format). Validity is against catalog requirements '
'is not checked, and is the responsibility of the '
'calling client.'),
})
add_document_through_url_response = document_ns.model('AddDocumentThroughURLResponse', {
'message': fields.String(description='Status message'),
'document_id': fields.Integer(description='ID of the created document'),
'document_version_id': fields.Integer(description='ID of the created document version'),
'task_id': fields.String(description='ID of the embedding task')
})
@document_ns.route('/add_document_through_url')
class AddDocumentThroughURL(Resource):
@jwt_required()
@requires_service('DOCAPI')
@document_ns.expect(add_document_through_url)
@document_ns.response(201, 'Document added successfully', add_document_through_url)
@document_ns.response(400, 'Validation Error')
@document_ns.response(422, 'File could not be processed')
@document_ns.response(500, 'Internal Server Error')
def post(self):
"""
Add a new document using a URL. The URL can be temporary, and will not be stored.
Mainly used for passing temporary URLs like used in e.g. Zapier
"""
tenant_id = get_jwt_identity()
current_app.logger.info(f'Adding document through url for tenant {tenant_id}')
try:
args = document_ns.payload
except Exception as e:
current_app.logger.error(f"Error parsing arguments: {str(e)}")
current_app.logger.error(f"Exception type: {type(e)}")
raise
file_url = args['temp_url']
current_app.logger.info(f"Downloading file from URL: {file_url}")
try:
response = requests.get(file_url, stream=True)
response.raise_for_status()
# Get filename from URL or use provided name
filename = secure_filename(args.get('name') or file_url.split('/')[-1])
extension = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''
# Create FileStorage object from downloaded content
file_content = io.BytesIO(response.content)
file = FileStorage(
stream=file_content,
filename=filename,
content_type=response.headers.get('content-type', 'application/octet-stream')
)
current_app.logger.info(f"Successfully downloaded file: {filename}")
except requests.RequestException as e:
current_app.logger.error(f"Error downloading file: {str(e)}")
return {'message': f'Error downloading file: {str(e)}'}, 422
try:
# Prepare API input
api_input = {
'catalog_id': args.get('catalog_id'),
'name': args.get('name') or filename,
'language': args.get('language'),
'user_context': args.get('user_context'),
'valid_from': args.get('valid_from'),
'user_metadata': args.get('user_metadata'),
'catalog_properties': args.get('catalog_properties'),
}
new_doc, new_doc_vers = create_document_stack(api_input, file, filename, extension, tenant_id)
task_id = start_embedding_task(tenant_id, new_doc_vers.id)
return {
'message': f'Processing on document {new_doc.name}, version {new_doc_vers.id} started. Task ID: {task_id}.',
'document_id': new_doc.id,
'document_version_id': new_doc_vers.id,
'task_id': task_id
}, 201
except (EveAIInvalidLanguageException, EveAIUnsupportedFileType) as e:
current_app.logger.error(f'Error adding document: {str(e)}')
return {'message': str(e)}, 400
except Exception as e:
current_app.logger.error(f'Error adding document: {str(e)}')
return {'message': 'Error adding document'}, 500
# Models for AddURL
add_url_model = document_ns.model('AddURL', {
'catalog_id': fields.Integer(required='True', description='ID of the catalog the URL needs to be added to'),

View File

@@ -1,5 +1,5 @@
# Import all processor implementations to ensure registration
from . import audio_processor, html_processor, pdf_processor
from . import audio_processor, html_processor, pdf_processor, markdown_processor, docx_processor
# List of all available processor implementations
__all__ = ['audio_processor', 'html_processor', 'pdf_processor']
__all__ = ['audio_processor', 'html_processor', 'pdf_processor', 'markdown_processor', 'docx_processor']

View File

@@ -117,8 +117,7 @@ class EveAI_API {
throw new Exception("API error ({$error_type}): {$error_message}");
}
return $response_data
// return $body;
return $body;
} catch (Exception $e) {
error_log("EveAI API Exception: " . $e->getMessage());
throw $e;

View File

@@ -0,0 +1,4 @@
{
"id": 216725,
"key": "App216725"
}

View 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.

View 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;

View 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,
};

View 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;
// }
// }
}
};

View 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: {},
};

File diff suppressed because it is too large Load Diff

View 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
}