Skip to content
This repository has been archived by the owner on Jan 9, 2024. It is now read-only.

Commit

Permalink
feat: Added channel to specify a release channel
Browse files Browse the repository at this point in the history
Fixes #248
  • Loading branch information
welwood08 committed Feb 12, 2018
1 parent f3769db commit 50fbf01
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 1 deletion.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -46,6 +46,9 @@ signAddon(
// WebExtensions do not require an ID.
// See the notes below about dealing with IDs.
id: 'your-addon-id@somewhere',
// The release channel (listed or unlisted).
// Default: most recently used channel.
channel: undefined,
// Save downloaded files to this directory.
// Default: current working directory.
downloadDir: undefined,
Expand Down
6 changes: 5 additions & 1 deletion src/amo-client.js
Expand Up @@ -77,12 +77,13 @@ export class Client {
* - `xpiPath` Path to xpi file.
* - `guid` Optional add-on GUID, aka the ID in install.rdf.
* - `version` add-on version string.
* - `channel` release channel (listed, unlisted).
* @return {Promise} signingResult with keys:
* - success: boolean
* - downloadedFiles: Array of file objects
* - id: string identifier for the signed add-on
*/
sign({guid, version, xpiPath}) {
sign({guid, version, channel=null, xpiPath}) {

const formData = {
upload: this._fs.createReadStream(xpiPath),
Expand All @@ -93,6 +94,9 @@ export class Client {
// PUT to a specific URL for this add-on + version.
addonUrl += encodeURIComponent(guid) +
"/versions/" + encodeURIComponent(version) + "/";
if (channel) {
formData.channel = channel;
}
} else {
// POST to a generic URL to create a new add-on.
this.debug("Signing add-on without an ID");
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Expand Up @@ -14,6 +14,9 @@ export default function signAddon(
id,
// The add-on version number for AMO.
version,
// The release channel on AMO (listed or unlisted).
// Defaults to most recently used channel.
channel=null,
// Your API key (JWT issuer) from AMO Devhub.
apiKey,
// Your API secret (JWT secret) from AMO Devhub.
Expand Down Expand Up @@ -87,6 +90,7 @@ export default function signAddon(
xpiPath: xpiPath,
guid: id,
version: version,
channel: channel,
});

});
Expand Down
76 changes: 76 additions & 0 deletions tests/test.amo-client.js
Expand Up @@ -138,6 +138,8 @@ describe("amoClient.Client", function() {
expect(putCall.conf.formData.upload).to.be.equal("fake-read-stream");
// When doing a PUT, the version is in the URL not the form data.
expect(putCall.conf.formData.version).to.be.undefined;
// When no channel is supplied, the API is expected to use the most recent channel.
expect(putCall.conf.formData.channel).to.be.undefined;

expect(waitForSignedAddon.called).to.be.equal(true);
expect(waitForSignedAddon.firstCall.args[0])
Expand Down Expand Up @@ -171,6 +173,80 @@ describe("amoClient.Client", function() {
expect(call.conf.url).to.match(/\/addons\/$/);
expect(call.conf.formData.upload).to.be.equal("fake-read-stream");
expect(call.conf.formData.version).to.be.equal(conf.version);
// Channel is not a valid parameter for new add-ons.
expect(call.conf.formData.channel).to.be.undefined;

expect(waitForSignedAddon.called).to.be.equal(true);
expect(waitForSignedAddon.firstCall.args[0])
.to.be.equal(apiStatusUrl);
});
});

it("lets you sign an unlisted add-on", function() {
var apiStatusUrl = "https://api/addon/version/upload/abc123";
var conf = {
guid: "a-guid",
version: "a-version",
channel: "unlisted",
};
var waitForSignedAddon = sinon.spy(() => {});
this.client.waitForSignedAddon = waitForSignedAddon;

this.client._request = new MockRequest({
httpResponse: {statusCode: 202},
// Partial response like:
// http://olympia.readthedocs.org/en/latest/topics/api/signing.html#checking-the-status-of-your-upload
responseBody: {
url: apiStatusUrl,
},
});

return this.sign(conf).then(() => {
var putCall = this.client._request.calls[0];
expect(putCall.name).to.be.equal("put");

var partialUrl = "/addons/" + conf.guid + "/versions/" + conf.version;
expect(putCall.conf.url).to.include(partialUrl);
expect(putCall.conf.formData.upload).to.be.equal("fake-read-stream");
// When doing a PUT, the version is in the URL not the form data.
expect(putCall.conf.formData.version).to.be.undefined;
expect(putCall.conf.formData.channel).to.be.equal("unlisted");

expect(waitForSignedAddon.called).to.be.equal(true);
expect(waitForSignedAddon.firstCall.args[0])
.to.be.equal(apiStatusUrl);
});
});

it("lets you sign a listed add-on", function() {
var apiStatusUrl = "https://api/addon/version/upload/abc123";
var conf = {
guid: "a-guid",
version: "a-version",
channel: "listed",
};
var waitForSignedAddon = sinon.spy(() => {});
this.client.waitForSignedAddon = waitForSignedAddon;

this.client._request = new MockRequest({
httpResponse: {statusCode: 202},
// Partial response like:
// http://olympia.readthedocs.org/en/latest/topics/api/signing.html#checking-the-status-of-your-upload
responseBody: {
url: apiStatusUrl,
},
});

return this.sign(conf).then(() => {
var putCall = this.client._request.calls[0];
expect(putCall.name).to.be.equal("put");

var partialUrl = "/addons/" + conf.guid + "/versions/" + conf.version;
expect(putCall.conf.url).to.include(partialUrl);
expect(putCall.conf.formData.upload).to.be.equal("fake-read-stream");
// When doing a PUT, the version is in the URL not the form data.
expect(putCall.conf.formData.version).to.be.undefined;
expect(putCall.conf.formData.channel).to.be.equal("listed");

expect(waitForSignedAddon.called).to.be.equal(true);
expect(waitForSignedAddon.firstCall.args[0])
Expand Down
10 changes: 10 additions & 0 deletions tests/test.sign.js
Expand Up @@ -97,6 +97,16 @@ describe("sign", function() {
});
});

it("passes release channel to the signer", () => {
const channel = "listed";
return runSignCmd({
cmdOptions: {channel},
}).then(function() {
expect(signingCall.called).to.be.equal(true);
expect(signingCall.firstCall.args[0].channel).to.be.equal(channel);
});
});

it("passes JWT expiration to the signing client", () => {
const expiresIn = 60 * 15; // 15 minutes
return runSignCmd({
Expand Down

0 comments on commit 50fbf01

Please sign in to comment.