From ff4dbae8a9edf4f72d5b486fd48678d4e63fdd4d Mon Sep 17 00:00:00 2001 From: Troy Simeon Taylor <44444967+troystaylor@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:46:16 -0400 Subject: [PATCH] Add files via upload --- .../iLovePDF/apiDefinition.swagger.json | 143 +++++++++++++++++ .../iLovePDF/apiProperties.json | 25 +++ .../iLovePDF/readme.md | 25 +++ .../iLovePDF/script.csx | 146 ++++++++++++++++++ 4 files changed, 339 insertions(+) create mode 100644 independent-publisher-connectors/iLovePDF/apiDefinition.swagger.json create mode 100644 independent-publisher-connectors/iLovePDF/apiProperties.json create mode 100644 independent-publisher-connectors/iLovePDF/readme.md create mode 100644 independent-publisher-connectors/iLovePDF/script.csx diff --git a/independent-publisher-connectors/iLovePDF/apiDefinition.swagger.json b/independent-publisher-connectors/iLovePDF/apiDefinition.swagger.json new file mode 100644 index 0000000000..3276563ff5 --- /dev/null +++ b/independent-publisher-connectors/iLovePDF/apiDefinition.swagger.json @@ -0,0 +1,143 @@ +{ + "swagger": "2.0", + "info": { + "title": "iLovePDF", + "description": "Whether you are a small startup or a large business, iLoveAPI is here to help you automate document processes. Our infrastructure uses the best technology for processing PDF and image files.", + "version": "1.0", + "contact": { + "name": "Troy Taylor", + "url": "https://www.hitachisolutions.com", + "email": "ttaylor@hitachisolutions.com" + } + }, + "host": "api.ilovepdf.com", + "basePath": "/", + "schemes": [ + "https" + ], + "consumes": [], + "produces": [], + "paths": { + "/processCloudFile": { + "get": { + "responses": { + "200": { + "description": "default", + "schema": { + "type": "string" + } + } + }, + "summary": "Process file from cloud URL", + "description": "Authenticate token, get server, upload file from cloud URL, start process, and download file.", + "operationId": "ProcessCloudFile", + "x-ms-visibility": "important", + "parameters": [ + { + "name": "tool", + "in": "header", + "required": false, + "type": "string", + "default": "compress", + "description": "PDF processing tool to use", + "x-ms-summary": "Tool" + }, + { + "name": "filename", + "in": "header", + "required": false, + "type": "string", + "default": "filename.pdf", + "description": "Output filename", + "x-ms-summary": "Filename" + }, + { + "name": "cloud_file", + "in": "header", + "required": true, + "type": "string", + "description": "Public URL of the file to process", + "x-ms-summary": "Cloud File URL" + } + ] + } + }, + "/processLocalFile": { + "post": { + "consumes": [ + "multipart/form-data" + ], + "responses": { + "200": { + "description": "default", + "schema": { + "type": "string" + } + } + }, + "summary": "Process file", + "description": "Authenticate token, get server, upload local file, start process, and download file.", + "operationId": "ProcessLocalFile", + "x-ms-visibility": "important", + "parameters": [ + { + "name": "tool", + "in": "header", + "required": false, + "type": "string", + "default": "compress", + "description": "PDF processing tool to use", + "x-ms-summary": "Tool" + }, + { + "name": "filename", + "in": "header", + "required": false, + "type": "string", + "default": "filename.pdf", + "description": "Output filename", + "x-ms-summary": "Filename" + }, + { + "name": "file", + "in": "formData", + "required": true, + "type": "file", + "description": "Local file to upload and process", + "x-ms-summary": "File" + } + ] + } + } + }, + "definitions": {}, + "parameters": {}, + "responses": {}, + "securityDefinitions": { + "Public Key": { + "type": "apiKey", + "in": "header", + "name": "public_key" + } + }, + "security": [ + { + "Public Key": [] + } + ], + "tags": [], + "x-ms-connector-metadata": [ + { + "propertyName": "Website", + "propertyValue": "https://www.iloveapi.com/" + }, + { + "propertyName": "Privacy policy", + "propertyValue": "https://www.iloveapi.com/policy" + }, + { + "propertyName": "Categories", + "propertyValue": "Content and Files" + } + ] +} \ No newline at end of file diff --git a/independent-publisher-connectors/iLovePDF/apiProperties.json b/independent-publisher-connectors/iLovePDF/apiProperties.json new file mode 100644 index 0000000000..d4d0dd4e2c --- /dev/null +++ b/independent-publisher-connectors/iLovePDF/apiProperties.json @@ -0,0 +1,25 @@ +{ + "properties": { + "connectionParameters": { + "api_key": { + "type": "securestring", + "uiDefinition": { + "displayName": "Public Key", + "description": "The Public Key for this api", + "tooltip": "Provide your Public Key", + "constraints": { + "tabIndex": 2, + "clearText": false, + "required": "true" + } + } + } + }, + "iconBrandColor": "#da3b01", + "scriptOperations": [], + "capabilities": [], + "policyTemplateInstances": [], + "publisher": "Troy Taylor", + "stackOwner": "iLovePDF" + } +} \ No newline at end of file diff --git a/independent-publisher-connectors/iLovePDF/readme.md b/independent-publisher-connectors/iLovePDF/readme.md new file mode 100644 index 0000000000..a6f31c7f18 --- /dev/null +++ b/independent-publisher-connectors/iLovePDF/readme.md @@ -0,0 +1,25 @@ +# iLovePDF + +Whether you are a small startup or a large business, iLoveAPI is here to help you automate document processes. Our infrastructure uses the best technology for processing PDF and image files. + +## Publisher: Troy Taylor, Hitachi Solutions + +## Prerequisites +You must have an iLovePDF API account and obtain your Public Key from the [iLovePDF Developer Console](https://www.iloveapi.com/). + +## Obtaining Credentials +1. Visit the [iLovePDF API website](https://www.iloveapi.com/) +2. Sign up for a developer account +3. Navigate to your dashboard to find your Public Key +4. Use this Public Key to authenticate with the connector + +## Supported Operations + +### Process file from cloud URL +Authenticate token, get server, upload file from cloud URL, start process, and download file. This operation downloads a file from a publicly accessible URL and processes it with the specified tool. + +### Process file +Authenticate token, get server, upload local file, start process, and download file. This operation allows you to upload a local file directly and process it with the specified tool. + +## Known Issues and Limitations +There are no known issues at this time. diff --git a/independent-publisher-connectors/iLovePDF/script.csx b/independent-publisher-connectors/iLovePDF/script.csx new file mode 100644 index 0000000000..8ecfaa8b1d --- /dev/null +++ b/independent-publisher-connectors/iLovePDF/script.csx @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +public class Script : ScriptBase +{ + public override async Task ExecuteAsync() + { + var _logger = this.Context.Logger; + + var authURL = "https://api.ilovepdf.com/v1/auth"; + var startURL = "https://api.ilovepdf.com/v1/start/{tool}"; + var uploadURL = "https://{server}/v1/upload"; + var processURL = "https://{server}/v1/process"; + var downloadURL = "https://{server}/v1/download/{task}"; + + var authorizationHeader = this.Context.Request.Headers.GetValues("public_key").FirstOrDefault(); + var toolHeader = this.Context.Request.Headers.GetValues("tool").FirstOrDefault(); + var cloudFileHeader = this.Context.Request.Headers.Contains("cloud_file") ? + this.Context.Request.Headers.GetValues("cloud_file").FirstOrDefault() : null; + var filenameHeader = this.Context.Request.Headers.GetValues("filename").FirstOrDefault(); + + // Determine operation type + var requestPath = this.Context.Request.RequestUri.AbsolutePath; + var isLocalFileUpload = requestPath.Contains("processLocalFile"); + + _logger.LogInformation($"Request path: {requestPath}"); + _logger.LogInformation($"Is local file upload: {isLocalFileUpload}"); + + var authtUrl = new Uri(authURL); + HttpRequestMessage authRequest = new HttpRequestMessage(HttpMethod.Post, authtUrl); + var authRequestBody = new Dictionary + { + { "public_key", authorizationHeader } + }; + var authRequestBodyEncoded = new FormUrlEncodedContent(authRequestBody); + authRequest.Content = authRequestBodyEncoded; + HttpResponseMessage authResponse = await this.Context.SendAsync(authRequest, this.CancellationToken); + var responseString = await authResponse.Content.ReadAsStringAsync(); + var jsonResponse = JObject.Parse(responseString); + jsonResponse.TryGetValue("token", out JToken signedToken); + var signedBearerToken = AuthenticationHeaderValue.Parse("Bearer " + signedToken.ToString()); + + var starttUrl = new Uri(startURL.Replace("{tool}", toolHeader)); + HttpRequestMessage startRequest = new HttpRequestMessage(HttpMethod.Get, starttUrl); + startRequest.Headers.Authorization = signedBearerToken; + + HttpResponseMessage startResponse = await this.Context.SendAsync(startRequest, this.CancellationToken); + startResponse.EnsureSuccessStatusCode(); + + var startResponseString = await startResponse.Content.ReadAsStringAsync(); + var startJsonResponse = JObject.Parse(startResponseString); + startJsonResponse.TryGetValue("server", out JToken serverToken); + var server = serverToken.ToString(); + startJsonResponse.TryGetValue("task", out JToken taskToken); + var task = taskToken.ToString(); + startJsonResponse.TryGetValue("remaining_files", out JToken remainingFilesToken); + var remainingFiles = remainingFilesToken.ToObject(); + + var upldUrl = new Uri(uploadURL.Replace("{server}", server)); + _logger.LogInformation($"Upload URL: {upldUrl}"); + HttpRequestMessage uploadRequest = new HttpRequestMessage(HttpMethod.Post, upldUrl); + _logger.LogInformation($"Upload request: {uploadRequest}"); + uploadRequest.Headers.Authorization = signedBearerToken; + _logger.LogInformation($"Authorization header: {signedBearerToken}"); + + var uploadRequestBody = new MultipartFormDataContent(); + uploadRequestBody.Add(new StringContent(task), "task"); + + string serverFilename; + + if (isLocalFileUpload) + { + // Handle local file upload + _logger.LogInformation("Processing local file upload"); + + // Get the uploaded file from the request + var fileContent = await this.Context.Request.Content.ReadAsByteArrayAsync(); + var byteArrayContent = new ByteArrayContent(fileContent); + byteArrayContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream"); + + uploadRequestBody.Add(byteArrayContent, "file", filenameHeader); + _logger.LogInformation($"Added local file: {filenameHeader}"); + } + else + { + // Handle cloud file upload (existing logic) + _logger.LogInformation("Processing cloud file upload"); + var cloudFile = cloudFileHeader.ToString(); + uploadRequestBody.Add(new StringContent(cloudFile), "cloud_file"); + _logger.LogInformation($"Added cloud file: {cloudFile}"); + } + + _logger.LogInformation($"Upload request body: {uploadRequestBody}"); + uploadRequest.Content = uploadRequestBody; + _logger.LogInformation($"Upload request content: {uploadRequest.Content}"); + var uploadResponse = await this.Context.SendAsync(uploadRequest, this.CancellationToken); + _logger.LogInformation($"Upload response: {uploadResponse}"); + var uploadResponseString = await uploadResponse.Content.ReadAsStringAsync(); + var uploadJsonResponse = JObject.Parse(uploadResponseString); + uploadJsonResponse.TryGetValue("server_filename", out JToken serverFilenameID); + serverFilename = serverFilenameID.ToString(); + + var processUrl = new Uri(processURL.Replace("{server}", server)); + _logger.LogInformation($"Process URL: {processUrl}"); + HttpRequestMessage processRequest = new HttpRequestMessage(HttpMethod.Post, processUrl); + _logger.LogInformation($"Process request: {processRequest}"); + processRequest.Headers.Authorization = signedBearerToken; + _logger.LogInformation($"Authorization header: {signedBearerToken}"); + var processRequestBodyObject = new JObject + { + { "task", task }, + { "tool", toolHeader }, + { "files", new JArray(new JObject + { + { "server_filename", serverFilename }, + { "filename", filenameHeader } + }) + } + }; + var processRequestBodyString = processRequestBodyObject.ToString(); + _logger.LogInformation($"Process request JSON body: {processRequestBodyString}"); + var processRequestBody = new MultipartFormDataContent(); + processRequest.Content = new StringContent(processRequestBodyString, Encoding.UTF8, "application/json"); + _logger.LogInformation($"Process request body: {processRequestBody}"); + _logger.LogInformation($"Process request content: {processRequest.Content}"); + var processResponse = await this.Context.SendAsync(processRequest, this.CancellationToken); + _logger.LogInformation($"Process response: {processResponse}"); + + var downloadUrl = new Uri(downloadURL.Replace("{server}", server).Replace("{task}", task)); + _logger.LogInformation($"Download URL: {downloadUrl}"); + HttpRequestMessage downloadRequest = new HttpRequestMessage(HttpMethod.Get, downloadUrl); + _logger.LogInformation($"Download request: {downloadRequest}"); + downloadRequest.Headers.Authorization = signedBearerToken; + _logger.LogInformation($"Authorization header: {signedBearerToken}"); + var downloadResponse = await this.Context.SendAsync(downloadRequest, this.CancellationToken); + _logger.LogInformation($"Download response: {downloadResponse}"); + + return downloadResponse; + } +} \ No newline at end of file