How do I add headers to form data that has a progress bar?
I get the following error:
ERROR in src/app/services/auth.service.ts(91,23): error TS2554:
Expected 2-4 arguments, but got 5.
Code:
public upload(
files: Set<File>
): { [key: string]: { progress: Observable<number> } } {
// this will be the our resulting map
const status: { [key: string]: { progress: Observable<number> } } = {};
files.forEach(file => {
// create a new multipart-form for every file
const formData: FormData = new FormData();
formData.append('file', file, file.name);
// formData.append('name', course, course.name);
// formData.append('text', username, username.name);
let headers = new Headers();
this.loadToken();
headers.append('Authorization', this.authToken);
// headers.append('Content-type', undefined);
// create a http-post request and pass the form
// tell it to report the upload progress
const req = new HttpRequest('POST', 'users/upload', formData,{headers: headers},{
reportProgress: true
});
// create a new progress-subject for every file
const progress = new Subject<any>();
// send the http-request and subscribe for progress-updates
const startTime = new Date().getTime();
this.https.request(req).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
// calculate the progress percentage
const percentDone = Math.round((100 * event.loaded) / event.total);
// pass the percentage into the progress-stream
progress.next(percentDone);
} else if (event instanceof HttpResponse) {
// Close the progress-stream if we get an answer form the API
// The upload is complete
progress.complete();
}
});
// Save every progress-observable in a map of all observables
status[file.name] = {
progress: progress.asObservable()
};
});
// return the map of progress.observables
return status;
}
ERROR in src/app/services/auth.service.ts(91,23): error TS2554:
Expected 2-4 arguments, but got 5.
The error message says that it expected 2-4 arguments, but got 5.
headers and reportProgress shouldn't be separate arguments, they both should be part of the fourth argument in HttpRequest.
To fix the error, change the HttpRequest as shown below:
const req = new HttpRequest('POST', 'users/upload', formData,
{ headers: headers, reportProgress: true });
Related
Hi I am trying to use Next.js api routes to store an image file on NFT.storage.
I am taking an image as an input from the user in the browser and passing that to the api file using Axios and FormData
const formData = new FormData(); formData.append('image', imageUrl); const cid = await axios.post('api/uploadNFTData', formData);
here the imageUrl is event.target.files[0]
and in the api folder I have uploadNFTData.js containing this code
I have done this to keep my API key more secure by taking the value from the .env
import { NFTStorage, File, Blob } from 'nft.storage';
import formidable from 'formidable';
const client = new NFTStorage({ token: process.env.NFT_STORE_TOKEN});
// first we need to disable the default body parser
export const config = {
api: {
bodyParser: false,
},
}
export default async function uploadNFTData(req,res) {
const form = new formidable.IncomingForm();
form.uploadDir = "./";
form.keepExtensions = true;
var formfields = await new Promise(function (resolve, reject) {
form.parse(req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
resolve(files);
console.log("within form.parse method, subject field of fields object is: " + files);
}); // form.parse
});
try{
const cid = await client.storeDirectory([formfields]);
res.status(200).json({ message: cid });
}catch(err){
res.status(500).json({ message: "could not upload data" });
}
}
Now I am getting the following error on server side
error - TypeError: blob.stream is not a function
Can someone please help regarding what I am doing wrong??
Or This kind of call cannot be done??
I'm needing to interact with the Flickr api from a cloudflare worker, but it's turning out to be exceedingly tricky.
My initial idea was to reach for the oauth1.0a library, but unfortunately it requires being passed a synchronous signature function. This is an issue because I need to use WebCrypto on the worker and it only exposes an asynchronous API.
Are there any other libraries I can use? I've currently spent hours trying to manually craft the request but keep getting errors saying the signature is bad.
This is my current attempt using someone's fork of oauth1.0a that adds support for an async signature function. This currently results in an "invalid_signature" response:
import OAuth from 'oauth-1.0a';
const CALLBACK_URL = "https://localhost:3000/oauth/callback";
const encoder = new TextEncoder();
async function signData(baseString: string, keyString: string) {
return await crypto.subtle.importKey(
'raw',
encoder.encode(keyString),
{ name: 'HMAC', hash: 'SHA-1' },
false,
['sign']
).then(key => {
return crypto.subtle.sign(
"HMAC",
key,
encoder.encode(baseString)
);
}).then(signature => {
let b = new Uint8Array(signature);
// base64 digest
return btoa(String.fromCharCode(...b));
});
}
export async function getRequestToken(consumerKey: string, consumerSecret: string) {
const url = "https://www.flickr.com/services/oauth/request_token";
const token = {
key: consumerKey,
secret: consumerSecret
}
const oauth = new OAuth({
consumer: token,
signature_method: 'HMAC-SHA1',
// #ts-ignore
hash_function: signData,
});
const requestData = {
url,
method: 'GET',
data: {
oauth_callback: CALLBACK_URL
}
};
// #ts-ignore
const authorisedRequest = await oauth.authorizeAsync(requestData, token);
let params = new URLSearchParams();
for (let [key, value] of Object.entries(authorisedRequest)) {
params.append(key, value as string);
}
const response = await fetch(requestData.url + `?${params}`, {
method: requestData.method,
});
const body = await response.text();
const parsedBody = oauth.deParam(body);
return parsedBody;
}
I apologize if this is unclear, it's late and I don't know how best to explain it.
I'm using an event emitter to pass data from a server response to a function inside of a separate class in another file, but when trying to use methods in those classes, the this keyword obviously doesn't work (because in this scenario, this refers to the server event emitter) - how would I reference a function within the class itself? I've provided code to help illustrate my point a bit better
ServiceClass.js
class StreamService {
/**
*
* #param {} database
* #param {Collection<Guild>} guilds
*/
constructor (database, guilds,) {
.....
twitchListener.on('live', this.sendLiveAlert) // fire test method when we get a notification
// if there are streamers to monitor, being monitoring
winston.info('Stream service initialized')
}
..............
async get (url, params = null, headers = this.defaultHeaders) {
// check oauth token
const expirationDate = this.token.expires_in || 0
if (expirationDate <= Date.now() || !this.token) await this.getAccessToken()
// build URL
const index = 0
let paramsString = ''
for (const [key, value] of params.entries()) {
if (index === 0) {
paramsString += `?${key}=${value}`
} else {
paramsString += `&${key}=${value}`
}
}
const res = await fetch(url + paramsString, { method: 'GET', headers: headers })
if (!res.ok) {
winston.error(`Error performing GET request to ${url}`)
return null
}
return await res.json()
}
async sendLiveAlert(streamTitle, streamURL, avatar, userName, gameId, viewerCount, thumbnail, startDateTime) {
// get game name first (no headers needed)
const params = new Map()
params.set('id', gameId)
const gameData = await this.get('https://api.twitch.tv/heliix/games', params, this.defaultHeaders)
if(gameData) {
// get webhook and send message to channel
const webhookClient = new WebhookClient('755641606555697305', 'OWZvI01kUUf4AAIR9uv2z4CxRse3Ik8b0LKOluaOYKmhE33h0ypMLT0JJm3laomlZ05o')
const embed = new MessageEmbed()
.setTitle(`${userName} just went live on Twitch!`)
.setURL(streamURL)
.setThumbnail(avatar)
.addFields(
{ name: 'Now Playing', value: gameData.data[0].name },
{ name: 'Stream Title', value: streamTitle }
)
.setImage(thumbnail)
}
webhookClient.send('Webhook test', embed)
}
}
Server.js
class TwitchWebhookListener extends EventEmitter {
......................
// Routes
server
.post((req, res) => {
console.log('Incoming POST request on /webhooks')
............................
const data = req.body.data[0]
if(!this.streamerLiveStatus.get(data.user_id) && data.type === 'live') {
// pass request body to bot for processing
this.emit(
'live',
data.title, // stream title
`https://twitch.tv/${data.user_name}`, // channel link
`https://avatar.glue-bot.xyz/twitch/${data.user_name}`, // streamer avatar
data.user_name,
data.game_id,
data.viewer_count,
data.thumbnail_url,
data.started_at // do we need this?
)
}
break
default:
res.send(`Unknown webhook for ${req.params.id}`)
break
}
} else {
console.log('The Signature did not match')
res.send('Ok')
}
} else {
console.log('It didn\'t seem to be a Twitch Hook')
res.send('Ok')
}
})
}
}
const listener = new TwitchWebhookListener()
listener.listen()
module.exports = listener
Within the sendLiveAlert method, I'm trying to call the get method of the StreamService class - but because it's called directly via the emitter within server.js, this refers specifically to the Server.js class - is there any way I can use StreamService.get()? I could obviously just rewrite the code inside the method itself, but that seems unnecessary when its right there?
Change this:
twitchListener.on('live', this.sendLiveAlert)
to this:
twitchListener.on('live', this.sendLiveAlert.bind(this))
Or, you could also do this:
twitchListener.on('live', (...args) => {
this.sendLiveAlert(...args);
});
With .bind() it creates a function wrapper that resets the proper value of this for you. In the case of the arrow function, it preserves the lexical value of this for you.
I have inherited the following code. This is part of CICD pipeline. It tries to get an object called "changes" from a bucket and does something with it. If it is able to grab the object, it sends a success message back to pipeline. If it fails to grab the file for whatever reason, it sends a failure message back to codepipeline.
This "changes" file is made in previous step of the codepipeline. However, sometimes it is valid for this file NOT to exist (i.e. when there IS no change).
Currently, the following code makes no distinction if file simply does not exist OR some reason code failed to get it (access denied etc.)
Desired:
I would like to send a success message back to codepipeline if file is simply not there.
If there is access issue , then the current outcome of "failure' would still be valid.
Any help is greatly appreciated. Unfortunately I am not good enough with Javascript to have any ideas to try.
RELEVANT PARTS OF THE CODE
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const lambda = new AWS.Lambda();
const codePipeline = new AWS.CodePipeline();
// GET THESE FROM ENV Variables
const {
API_SOURCE_S3_BUCKET: s3Bucket,
ENV: env
} = process.env;
const jobSuccess = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobSuccessResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
const jobFailure = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobFailureResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
// MAIN CALLER FUNCTION. STARTING POINT
exports.handler = async (event, context, callback) => {
try {
// WHAT IS IN changes file in S3
let changesFile = await getObject(s3, s3Bucket, `lambda/${version}/changes`);
let changes = changesFile.trim().split("\n");
console.log("List of Changes");
console.log(changes);
let params = { jobId };
let jobSuccessResponse = await jobSuccess(codePipeline, params);
context.succeed("Job Success");
}
catch (exception) {
let message = "Job Failure (General)";
let failureParams = {
jobId,
failureDetails: {
message: JSON.stringify(message),
type: "JobFailed",
externalExecutionId: context.invokeid
}
};
let jobFailureResponse = await jobFailure(codePipeline, failureParams);
console.log(message, exception);
context.fail(`${message}: ${exception}`);
}
};
S3 should return an error code in the exception:
The ones you care about are below:
AccessDenied - Access Denied
NoSuchKey - The specified key does not exist.
So in your catch block you should be able to validate exception.code to check if it matches one of these 2.
I want to pass original file stream pass down to other layer of code which will handle later drop on disk (upload to cloud storage) behavior. As files size might be large I can't actually fully buffer incoming file. I assume that PassThrough stream should pass needed data. While file.resume already called, finish event never get called.
How can I collect all required form fields along with single file stream and make proper service call, without explicit whole file in memory storage or on local disk, as I have a few of both of them?
private collectMultipartRequest (req: Request, fileFieldName: string): Promise<{ file: IFile, fields: { [k: string]: string }}> {
const obj = {
file: null,
fields: {}
};
return new Promise ((resolve, reject) => {
const busboy = new Busboy({ headers: req.headers, limits: { files: 1 }});
busboy.on("file", (fieldname, file, filename, mimetype) => {
if (fieldname === fileFieldName) {
const passThrough = new PassThrough();
file.pipe(passThrough);
obj.file = <IFile>{
mimeType: mimetype,
name: filename,
readStream: passThrough
};
}
file.resume();
});
busboy.on("field", (fieldName, val) => {
obj.fields[fieldName] = val;
});
busboy.on("filesLimit", () => {
reject(obj);
});
busboy.on("finish", async () => {
resolve(obj);
});
req.pipe(busboy);
});
}