Using Typescript and Azure Functions to send FormData containing a file using Axios

I'm currently facing an issue with my Azure Function that handles file uploads to the endpoint. I am attempting to repost the files to another endpoint microservice using Axios, but I keep getting an error stating "source.on is not a function" when trying to add formData to the ocrServicePromise.

Any insights on what could be causing this error?

Error details: [10/26/2020 2:06:27 PM] Executed 'Functions.ReceiveInboundFax' (Failed, Id=0a7bb949-9ff8-4762-8c21-ba2177ba96e2) [10/26/2020 2:06:27 PM] System.Private.CoreLib: Exception while executing function: Functions.ReceiveInboundFax. System.Private.CoreLib: Result: Failure [10/26/2020 2:06:27 PM] Exception: TypeError: source.on is not a function [10/26/2020 2:06:27 PM] Stack: TypeError: source.on is not a function

import { AzureFunction, Context, HttpRequest } from "@azure/functions"
import * as multipart from 'parse-multipart';
import axios from 'axios';
import { OcrStatusResponse } from "./classes/OcrStatusResponse";
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import gql from 'graphql-tag';
import fetch from 'node-fetch';
import { createHttpLink } from "apollo-link-http";

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {

const body = req.rawBody;
// Retrieve the boundary id
const boundary = multipart.getBoundary(req.headers["content-type"]);
let logEntries = [];

if (boundary) {
    const files: File[] = multipart.Parse(Buffer.from(body), boundary);

    if (files && files.length > 0) {
                      
        var allUpdated = await Promise.all(files.map(async (file) => {

            //CallOcrService
            var ocrresult = await CallOcrService(context, file);                

            //Write to Hasura
            logEntries.push(await LogOcrRequest(context, ocrresult, file.name));

        }));
    }   
    
}    

context.res = {
    status: 200,            
    body: logEntries
}  

};

async function CallOcrService(context: Context,file : File) : Promise<OcrStatusResponse>{

const FormDataFile = require('form-data');
const formData = new FormDataFile();
formData.append("file", file)

const ocrServicePromise = await axios.post(process.env["Ocr_Service_Upload_Endpoint"],  formData, {
        headers: formData.getHeaders()
    })
    .then((response) => {
        const ocrSeriviceResponse = new OcrStatusResponse(response.data);

        ocrSeriviceResponse.status = response.data.state;
        
        return ocrSeriviceResponse;
    }, (error) => {
        context.log.error(error);

        throw new Error(error);
    }
);

return ocrServicePromise;
}

async function LogOcrRequest(context: Context, ocrResponse: OcrStatusResponse, filename: String){  

const headers = {
    "content-type": "application/json",
    "x-hasura-admin-secret": process.env["ATTACHMENT_API_ADMIN_SECRET"]
}

const client = new ApolloClient({
    link: createHttpLink({
    uri:
        process.env["ATTACHMENT_API_ENDPOINT"],
        headers:headers,        
        fetch:fetch
    }),
    cache: new InMemoryCache()
});

const ocr_deadline = dateAdd(new Date(), "minute", 20);

const mutationInsertObj = {
    date_time_received: new Date(),
    file_name_received: filename,
    ocr_job_id: ocrResponse.jobid,
    date_time_ocr_response_deadline : ocr_deadline,
    ocr_result_statuscheck_endpoint : ocrResponse.status_url,
    ocr_result : ocrResponse.status,
    ocr_document_endpoint: `${process.env["Ocr_Service_Upload_Document_Retrival_Endpoint"]}/${ocrResponse.jobid}/${ocrResponse.jobid}.pdf`
}

if(!ocrResponse.success){
    context.log.error(`Response was empty or did not receive a status url for ${filename}`);
    mutationInsertObj.ocr_result = "ERROR";
}

const insertMutation = gql `
    mutation InsertOcrRequest($ocrResponse: inbound_fax_insert_input!)  {
        insert_inbound_fax_one(
            object: $ocrResponse
        ) {
            inboundfaxid
            ocr_job_id
            ocr_result_statuscheck_endpoint
            ocr_result
            date_time_ocr_response_deadline
            ocr_document_endpoint
        }
    }
`;

const {errors,data} = await client.mutate({
    mutation: insertMutation,
    errorPolicy: 'all',
    variables: mutationInsertObj
});

if(errors!== undefined){
    const errorList = [];
    errorList.push(errors);
    return errorList;
}

return data;

}

function dateAdd(date, interval, units) {
if(!(date instanceof Date))
  return undefined;

var ret = new Date(date); 
var checkRollover = function() { if(ret.getDate() != date.getDate()) ret.setDate(0);};

switch(String(interval).toLowerCase()) {
  case 'year'   :  ret.setFullYear(ret.getFullYear() + units); checkRollover();  break;
  case 'quarter':  ret.setMonth(ret.getMonth() + 3*units); checkRollover();  break;
  case 'month'  :  ret.setMonth(ret.getMonth() + units); checkRollover();  break;
  case 'week'   :  ret.setDate(ret.getDate() + 7*units);  break;
  case 'day'    :  ret.setDate(ret.getDate() + units);  break;
  case 'hour'   :  ret.setTime(ret.getTime() + units*3600000);  break;
  case 'minute' :  ret.setTime(ret.getTime() + units*60000);  break;
  case 'second' :  ret.setTime(ret.getTime() + units*1000);  break;
  default       :  ret = undefined;  break;
}

return ret;
}



export default httpTrigger;

Answer №1

When utilizing the package parse-multipart to process multipart/form-data, it simply provides the value in this format:

{ filename: 'A.txt', type: 'text/plain', data: <Buffer 41 41 41 41 42 42 42 42> }
, without returning a File Object. More information can be found here
https://i.sstatic.net/ac7lZ.png

Moreover, the package form-data supports string, buffer, and file stream inputs only. For additional details, visit here.

To address these points, please proceed with the following steps:

  1. Create an interface for handling values received from parse-multipart
export interface FileInfo {
  filename: string;
  type: string;
  data: Buffer;
}
  1. Update the HTTP trigger code accordingly
import * as multipart from "parse-multipart";
import axios from "axios";
import { FileInfo } from "../entities/FileInfo";
import * as FormDataFile from "form-data";
const httpTrigger: AzureFunction = async function (
  context: Context,
  req: HttpRequest
): Promise<void> {
  context.log("HTTP trigger function processed a request.");
  const body = req.rawBody;
  // Retrieve the boundary id
  const boundary = multipart.getBoundary(req.headers["content-type"]);
  let logEntries = [];

  if (boundary) {
    const files: FileInfo[] = multipart.Parse(Buffer.from(body), boundary);

    if (files && files.length > 0) {
      var allUpdated = await Promise.all(
        files.map(async (file) => {
          await CallOcrService(context, file);
        })
      );
    }
  }

  context.res = {
    status: 200,
    body: "OK",
  };
};

async function CallOcrService(
  context: Context,
  file: FileInfo
): Promise<String> {
  const formData = new FormDataFile();
  formData.append("file", Buffer.from(file.data), {
    filename: file.filename,
    contentType: file.type,
  });

 // call you api
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using `reduce` in TypeScript, you can organize an array of objects based on a shared property

Here is an example of an array: [ { id: '1', task: 'Grocery shopping', isImportant: true }, { id: '2', task: 'Meeting', isImportant: false }, { id: '3', task: &apos ...

Sharing information between components in Angular through service communication

In my Angular 4 project, there is a functionality where upon clicking on one of the 'groups', its tile should be added to a list of 'favourites' which is represented as an array. To implement this feature, I utilized a BehaviorSubject. ...

Disabling aria-label enforcement for icon-buttons in Chakra UI Typescript

Having a Chakra UI icon button, I am facing an issue with the aria-label attribute. The IconButton is within an aria-hidden section in the tree; therefore, the label becomes redundant. If I try to remove the aria-label, TypeScript throws complaints as sho ...

Create a flexible string for refining a REST request

I am currently working on constructing a dynamic string and I've encountered an issue, so I humbly seek assistance from the community. I have a string for creating a rest call filter, but I am struggling with the use of and's This query only fu ...

Verification of unique custom string

How can I ensure that a string follows the specific format of x.xx.xxxxx? The first character is mandatory, followed by a period, then two characters, another period, and finally any number of characters of varying lengths. ...

TypeScript's type 'T' has the potential to be instantiated with any type, even if it is not directly related to 'number'

Let's say we have a function that takes a value (for example, number) and a callback function that maps that value to another value. The function simply applies the provided callback: function mapNumber<T>(value: number, mapfn: (value: number) = ...

What is the process for importing libraries from a different local directory?

What I mean by that title is: I have some code that was generated and now I am incorporating it into my Angular application. Currently, I am installing this code as a package using npm, but it is causing issues with my deployment setup. So, I would like ...

What is the method to make a String bold when sending it through a messaging service?

Here is the structure of my service: import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class MessageService { messages: string[] = []; add(message: string) { this.messages.push(message); ...

Incorporate my personalized icons into the button design of ionic 2 actionSheet

I'm struggling to figure out how to incorporate my personal icon into the actionSheet feature of ionic 2/3. presentActionSheet() { let actionSheet = this.actionSheetCtrl.create({ title: 'Mode', buttons: [ { ...

The rapid execution of code causing an observable race condition

When exporting a CSV file in my code, I encounter a race condition while trying to modify some data before the export. The issue is that the id gets set correctly, but the number only updates after the method is called a second time. I believe the proble ...

What is the best way to ensure every component in Angular 2 has access to a custom pipe?

I've come up with a unique idea to create a custom rainbowize pipe that wraps each letter in a span with a random color of the rainbow as the css color property. My goal is to implement this custom pipe across all components in my app without having t ...

ERROR: Unhandled promise rejection: Unable to find a matching route for URL Segment 'main/knowledge-base'

After setting up dynamic routing for my Angular 6 application, I encountered an error when clicking on a link (for example, 'knowledge base') that stated: core.js:1673 ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segme ...

You may encounter an error stating "Property X does not exist on type 'Vue'" when attempting to access somePropOrMethod on this.$parent or this.$root in your code

When using VueJS with TypeScript, trying to access a property or method using this.$parent.somePropOrMethod or this.$root.somePropOrMethod can lead to a type error stating that Property somePropOrMethod does not exist on type 'Vue' The defined i ...

Example showcasing the functionality of the react-custom-scrollbars package in a TypeScript React application

In my TypeScript React project, I am struggling to set up the react-custom-scrollbars package successfully. Despite consulting the examples provided in the GitHub repository, I have not been able to get it working. Can someone share a functional example ...

Angular - Utilizing NgRx selector for efficient data update notifications

Is there a method to create a "data updated" indicator when I am not interested in the actual updated data itself? Consider a scenario with a reducer: const initialState: SomeReducer = { dataInQuestion: Array<SomeDto>, ... } Following an action ...

While utilizing Ionic to upload images to a server, I encountered the FileTransferError error code 3

I have successfully uploaded an image from the gallery, but I am facing issues while uploading multiple images at once. Here is the code snippet I am using: pictureUpload(x){ // x represents the file path like - file:///storage/emulated/0/Download/palak ...

Creating a String-only pattern Validator: A step-by-step guide

Below is the code I've written: ***<input type="text" placeholder="First Name" name="firstName1" [(ngModel)]="firstName" #firstName1="ngModel" required pattern="^[a-z0-9_-]{8,15}$" >*** ...

The element does not recognize the property 'width' since it is not defined in the type of 'GlobalEventHandlers'

I'm trying to determine the size of an image using JavaScript, but I encountered a TypeScript error: const img = new Image(); img.onload = function() { alert(this.width + 'x' + this.height); } img.src = 'http://www.google.com/intl/en_ ...

Retrieve the HTML data from a form created with Angular

Here is an example of an Angular form: <div #myForm [formGroup]="myForm"> <select formControlName="productName" class="form-control"> <option value="">Select</option&g ...

Uh-oh! An unexpected type error occurred. It seems that the property 'paginator' cannot be set

I am developing a responsive table using Angular Material. To guide me, I found this helpful example here. Here is the progress I have made so far: HTML <mat-form-field> <input matInput (keyup)="applyFilter($event.target.value)" placeholder ...