Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/util/submit-addon.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ const log = createLogger(import.meta.url);

export const defaultAsyncFsReadFile = fsPromises.readFile;

// Appended to client errors (4xx) from submission requests, which are usually
// caused by missing or invalid metadata (e.g. no license for listed versions).
export const SUBMISSION_ERROR_HINT =
'The add-on metadata may be missing a required field or contain an invalid value. ' +
'Pass metadata with --amo-metadata, see ' +
'https://mozilla.github.io/addons-server/topics/api/addons.html#version-create ' +
'and https://extensionworkshop.com/documentation/develop/getting-started-with-web-ext/#sign-and-submit-for-publication';

export class JwtApiAuth {
#apiKey;
#apiSecret;
Expand Down Expand Up @@ -215,6 +223,7 @@ export default class Client {
'POST',
JSON.stringify(jsonData),
'Submission failed (1)',
SUBMISSION_ERROR_HINT,
);
}

Expand All @@ -229,6 +238,7 @@ export default class Client {
'PUT',
JSON.stringify(jsonData),
'Submission failed (2)',
SUBMISSION_ERROR_HINT,
);
}

Expand Down Expand Up @@ -306,7 +316,13 @@ export default class Client {
);
}

async fetchJson(url, method = 'GET', body, errorMsg = 'Bad Request') {
async fetchJson(
url,
method = 'GET',
body,
errorMsg = 'Bad Request',
errorHint,
) {
const response = await this.fetch(url, method, body);
if (response.status < 200 || response.status >= 500) {
throw new Error(
Expand All @@ -320,6 +336,7 @@ export default class Client {
[
`${errorMsg}: ${response.statusText || response.status}`,
JSON.stringify(data, null, 2),
...(errorHint ? [errorHint] : []),
].join('\n'),
);
}
Expand Down
83 changes: 83 additions & 0 deletions tests/unit/test-util/test.submit-addon.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Client, {
saveIdToFile,
saveUploadUuidToFile,
signAddon,
SUBMISSION_ERROR_HINT,
} from '../../../src/util/submit-addon.js';
import { withTempDir } from '../../../src/util/temp-dir.js';
import { consoleStream } from '../../../src/util/logger.js';
Expand Down Expand Up @@ -819,6 +820,33 @@ describe('util.submit-addon', () => {
sinon.match({ method: 'POST', body }),
);
});

it('includes a metadata hint when the submission is rejected', async () => {
const client = new Client(clientDefaults);
const errorBody = {
version: {
license: [
'This field, or custom_license, is required for listed versions.',
],
},
};
mockNodeFetch(
sinon.stub(client, 'nodeFetch'),
new URL('addons/addon/', baseUrl),
'POST',
[{ body: errorBody, status: 400 }],
);

const clientPromise = client.doNewAddonSubmit('some-uuid', {});
await assert.isRejected(
clientPromise,
[
'Submission failed (1): 400',
JSON.stringify(errorBody, null, 2),
SUBMISSION_ERROR_HINT,
].join('\n'),
);
});
});

describe('doNewAddonOrVersionSubmit', () => {
Expand Down Expand Up @@ -872,6 +900,44 @@ describe('util.submit-addon', () => {
sinon.match({ method: 'PUT', body }),
);
});

it('includes a metadata hint when the submission is rejected', async () => {
const client = new Client(clientDefaults);
const guid = '@some-addon-guid';
const errorBody = { version: { license: ['Invalid value.'] } };
mockNodeFetch(
sinon.stub(client, 'nodeFetch'),
new URL(`addons/addon/${guid}/`, baseUrl),
'PUT',
[{ body: errorBody, status: 400 }],
);

const clientPromise = client.doNewAddonOrVersionSubmit(
guid,
'some-uuid',
{},
);
await assert.isRejected(clientPromise, SUBMISSION_ERROR_HINT);
});

it('does not include the metadata hint on server errors', async () => {
const client = new Client(clientDefaults);
const guid = '@some-addon-guid';
mockNodeFetch(
sinon.stub(client, 'nodeFetch'),
new URL(`addons/addon/${guid}/`, baseUrl),
'PUT',
[{ body: {}, status: 500 }],
);

const clientPromise = client.doNewAddonOrVersionSubmit(
guid,
'some-uuid',
{},
);
const error = await clientPromise.catch((err) => err);
expect(error.message).to.equal('Submission failed (2): 500.');
});
});

describe('doFormDataPatch', () => {
Expand Down Expand Up @@ -1249,6 +1315,23 @@ describe('util.submit-addon', () => {
await assert.isRejected(clientPromise, 'Bad Request: 400\n{}');
});

it('appends the error hint on not ok responses', async () => {
mockNodeFetch(nodeFetchStub, baseUrl, 'GET', [
{ body: {}, status: 400 },
]);
const clientPromise = client.fetchJson(
baseUrl,
'GET',
undefined,
'Bad Request',
'Some hint',
);
await assert.isRejected(
clientPromise,
'Bad Request: 400\n{}\nSome hint',
);
});

it('rejects with a promise on < 100 responses', async () => {
mockNodeFetch(nodeFetchStub, baseUrl, 'GET', [
{ body: {}, status: 99 },
Expand Down