I am trying to follow https://blog.mozilla.org/addons/2022/03/17/new-api-for-submitting-and-updating-add-ons/ to upload an addon file. It works fine with cURL but I really want to use Node.js (stuck on 16 at the moment) since I have to build a JWT. I am using the --experimental-fetch
flag with node and am trying to avoid adding any more dependencies.
Here’s what I have so far but the endpoint is complaining that the “upload” key is missing from the form data even though I am adding the file stream to the form data using the “upload” key.
<code>const jwt = require('jsonwebtoken');
const fs = require('fs');
var issuedAt = Math.floor(Date.now() / 1000);
var payload = {
iss: process.env.WEB_EXT_API_KEY,
jti: Math.random().toString(),
iat: issuedAt,
exp: issuedAt + 60,
};
var secret = process.env.WEB_EXT_API_SECRET; // store this securely.
var token = jwt.sign(payload, secret, {
algorithm: 'HS256', // HMAC-SHA256 signing algorithm
});
async function main() {
const manifest = require('../manifest.json');
const file_path = `web-ext-artifacts/myaddon-${manifest.version}.zip`
const fileBuffer = fs.createReadStream(file_path); // should this be something else?
const form = new FormData();
form.append('upload', fileBuffer); // Is this wrong?
form.append('channel', 'listed');
console.log('formData', form);
const url = 'https://addons.mozilla.org/api/v5/addons/upload/'
const req = new Request(url, {
method: 'POST',
body: form,
})
req.headers.append('Authorization', `JWT ${token}`);
req.headers.append('Accept', 'application/json');
fetch(req).then(async (res) => {
// console.log('response:', res);
console.log('response.status:', res.status);
const resData = await res.json();
console.log('response.body:', resData);
if (!res.ok) {
throw new Error(`HTTP error! Status: ${res.status}`);
}
})
.catch(err => {
console.error(err);
});
}
main().catch(err => {
console.log(err)
})
</code>
<code>const jwt = require('jsonwebtoken');
const fs = require('fs');
var issuedAt = Math.floor(Date.now() / 1000);
var payload = {
iss: process.env.WEB_EXT_API_KEY,
jti: Math.random().toString(),
iat: issuedAt,
exp: issuedAt + 60,
};
var secret = process.env.WEB_EXT_API_SECRET; // store this securely.
var token = jwt.sign(payload, secret, {
algorithm: 'HS256', // HMAC-SHA256 signing algorithm
});
async function main() {
const manifest = require('../manifest.json');
const file_path = `web-ext-artifacts/myaddon-${manifest.version}.zip`
const fileBuffer = fs.createReadStream(file_path); // should this be something else?
const form = new FormData();
form.append('upload', fileBuffer); // Is this wrong?
form.append('channel', 'listed');
console.log('formData', form);
const url = 'https://addons.mozilla.org/api/v5/addons/upload/'
const req = new Request(url, {
method: 'POST',
body: form,
})
req.headers.append('Authorization', `JWT ${token}`);
req.headers.append('Accept', 'application/json');
fetch(req).then(async (res) => {
// console.log('response:', res);
console.log('response.status:', res.status);
const resData = await res.json();
console.log('response.body:', resData);
if (!res.ok) {
throw new Error(`HTTP error! Status: ${res.status}`);
}
})
.catch(err => {
console.error(err);
});
}
main().catch(err => {
console.log(err)
})
</code>
const jwt = require('jsonwebtoken');
const fs = require('fs');
var issuedAt = Math.floor(Date.now() / 1000);
var payload = {
iss: process.env.WEB_EXT_API_KEY,
jti: Math.random().toString(),
iat: issuedAt,
exp: issuedAt + 60,
};
var secret = process.env.WEB_EXT_API_SECRET; // store this securely.
var token = jwt.sign(payload, secret, {
algorithm: 'HS256', // HMAC-SHA256 signing algorithm
});
async function main() {
const manifest = require('../manifest.json');
const file_path = `web-ext-artifacts/myaddon-${manifest.version}.zip`
const fileBuffer = fs.createReadStream(file_path); // should this be something else?
const form = new FormData();
form.append('upload', fileBuffer); // Is this wrong?
form.append('channel', 'listed');
console.log('formData', form);
const url = 'https://addons.mozilla.org/api/v5/addons/upload/'
const req = new Request(url, {
method: 'POST',
body: form,
})
req.headers.append('Authorization', `JWT ${token}`);
req.headers.append('Accept', 'application/json');
fetch(req).then(async (res) => {
// console.log('response:', res);
console.log('response.status:', res.status);
const resData = await res.json();
console.log('response.body:', resData);
if (!res.ok) {
throw new Error(`HTTP error! Status: ${res.status}`);
}
})
.catch(err => {
console.error(err);
});
}
main().catch(err => {
console.log(err)
})
Output when running the above:
<code>formData FormData {
[Symbol(state)]: [
{ name: 'upload', value: '[object Object]' },
{ name: 'channel', value: 'listed' }
]
}
(node:2723925) ExperimentalWarning: Fetch is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
response.status: 400
response.body: [ 'Missing "upload" key in multipart file data.' ]
Error: HTTP error! Status: 400
</code>
<code>formData FormData {
[Symbol(state)]: [
{ name: 'upload', value: '[object Object]' },
{ name: 'channel', value: 'listed' }
]
}
(node:2723925) ExperimentalWarning: Fetch is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
response.status: 400
response.body: [ 'Missing "upload" key in multipart file data.' ]
Error: HTTP error! Status: 400
</code>
formData FormData {
[Symbol(state)]: [
{ name: 'upload', value: '[object Object]' },
{ name: 'channel', value: 'listed' }
]
}
(node:2723925) ExperimentalWarning: Fetch is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
response.status: 400
response.body: [ 'Missing "upload" key in multipart file data.' ]
Error: HTTP error! Status: 400