Skip to content

Commit

Permalink
Add updateCommitStatus
Browse files Browse the repository at this point in the history
  • Loading branch information
d2lam committed Oct 19, 2016
1 parent a0a5023 commit 74d1eb0
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 19 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,25 @@ The contents of the file at `path` in the repository
1. Resolve with the contents of `path`
2. Reject if the `path` cannot be downloaded, decoded, or is not a file

### updateCommitStatus
The parameters required are:

| Parameter | Type | Required | Description |
| :------------- | :---- | :------- | :-------------|
| config | Object | true | Configuration Object |
| config.scmUri | String | true | The scm uri (ex: `bitbucket.org:batman/{1234}:branchName`) |
| config.token | String | true | Access token for scm |
| config.sha | String | true | The scm sha to update a status for |
| config.buildStatus | String | true | The screwdriver build status to translate into scm commit status |
| config.url | String | false | The target url for setting up details |

#### Expected Outcome
Update the commit status for a given repository and sha.

#### Expected Promise Response
1. Resolves to response when the commit status was updated
2. Reject if the commit status fails to update

## Testing

```bash
Expand Down
86 changes: 67 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ const MATCH_COMPONENT_HOSTNAME = 1;
const MATCH_COMPONENT_USER = 2;
const MATCH_COMPONENT_REPO = 3;
const MATCH_COMPONENT_BRANCH = 4;
const STATE_MAP = {
SUCCESS: 'SUCCESSFUL',
RUNNING: 'INPROGRESS',
QUEUED: 'INPROGRESS',
FAILURE: 'FAILED',
ABORTED: 'STOPPED'
};

/**
* Get repo information
Expand Down Expand Up @@ -183,8 +190,8 @@ class BitbucketScm extends Scm {
* to the master branch
* @method decorateUrl
* @param {Config} config Configuration object
* @param {String} config.scmUri The SCM URI the commit belongs to
* @param {String} config.token Service token to authenticate with Github
* @param {String} config.scmUri The scmUri
* @param {String} config.token Service token to authenticate with Bitbucket
* @return {Object} Resolves to a decoratedUrl with url, name, and branch
*/
_decorateUrl(config) {
Expand Down Expand Up @@ -215,8 +222,8 @@ class BitbucketScm extends Scm {
* @method _decorateCommit
* @param {Object} config Configuration object
* @param {Object} config.sha Commit sha to decorate
* @param {Object} config.scmUri SCM URI the commit belongs to
* @param {Object} config.token Service token to authenticate with Github
* @param {Object} config.scmUri The scmUri that the commit belongs to
* @param {Object} config.token Service token to authenticate with Bitbucket
* @return {Promise} Resolves to a decorated object with url, message, and author
*/
_decorateCommit(config) {
Expand Down Expand Up @@ -250,7 +257,7 @@ class BitbucketScm extends Scm {
* Get a commit sha for a specific repo#branch
* @method getCommitSha
* @param {Object} config Configuration
* @param {String} config.scmUri The scmUri to get commit sha of
* @param {String} config.scmUri The scmUri
* @param {String} config.token The token used to authenticate to the SCM
* @return {Promise} Resolves to the sha for the scmUri
*/
Expand All @@ -277,16 +284,18 @@ class BitbucketScm extends Scm {
* Fetch content of a file from Bitbucket
* @method getFile
* @param {Object} config Configuration
* @param {String} config.scmUri The scmUri to get permissions on
* @param {String} config.scmUri The scmUri
* @param {String} config.path The file in the repo to fetch
* @param {String} config.token The token used to authenticate to the SCM
* @param {String} config.ref The reference to the SCM, either branch or sha
* @return {Promise} Resolves to the content of the file
*/
_getFile(config) {
const scm = getScmUriParts(config.scmUri);
const fileUrl = `${API_URL_V1}/repositories/${scm.repoId}` +
`/src/${config.ref}/${config.path}?access_key=${config.token}`;
const urlRoot = `${API_URL_V1}/repositories/${scm.repoId}`;
const urlSuffix = `src/${config.ref}/${config.path}`;
const urlParameters = `access_key=${config.token}`;
const fileUrl = `${urlRoot}/${urlSuffix}?${urlParameters}`;
const options = {
url: fileUrl,
method: 'GET'
Expand All @@ -303,13 +312,13 @@ class BitbucketScm extends Scm {
}

/**
* Get a user's permissions on a repository
* @method _getPermissions
* @param {Object} config Configuration
* @param {String} config.scmUri The scmUri to get permissions on
* @param {String} config.token The token used to authenticate to the SCM
* @return {Promise} Resolves to permissions object with admin, push, pull
*/
* Get a user's permissions on a repository
* @method _getPermissions
* @param {Object} config Configuration
* @param {String} config.scmUri The scmUri
* @param {String} config.token The token used to authenticate to the SCM
* @return {Promise} Resolves to permissions object with admin, push, pull
*/
_getPermissions(config) {
const scm = getScmUriParts(config.scmUri);
const getPerm = (repoId, desiredAccess, token) => {
Expand Down Expand Up @@ -349,10 +358,49 @@ class BitbucketScm extends Scm {
}

/**
* Retrieve stats for the scm
* @method stats
* @param {Response} Object Object containing stats for the scm
*/
* Update the commit status for a given repo and sha
* @method updateCommitStatus
* @param {Object} config Configuration
* @param {String} config.scmUri The scmUri
* @param {String} config.sha The sha to apply the status to
* @param {String} config.buildStatus The screwdriver build status to translate into scm commit status
* @param {String} config.token The token used to authenticate to the SCM
* @param {String} config.url Target Url of this commit status
* @param {String} [config.jobName] Optional name of the job that finished
* @return {Promise}
*/
_updateCommitStatus(config) {
const scm = getScmUriParts(config.scmUri);
const options = {
url: `${REPO_URL}/${scm.repoId}/commit/${config.sha}/statuses/build`,
method: 'POST',
json: true,
body: {
url: config.url,
state: STATE_MAP[config.buildStatus],
key: config.sha,
description: config.jobName ? `Screwdriver/${config.jobName}` : 'Screwdriver'
},
auth: {
bearer: decodeURIComponent(config.token)
}
};

return this.breaker.runCommand(options)
.then((response) => {
if (response.statusCode !== 200) {
throw new Error(`STATUS CODE ${response.statusCode}: ${response.body}`);
}

return response;
});
}

/**
* Retrieve stats for the scm
* @method stats
* @param {Response} Object Object containing stats for the scm
*/
stats() {
return this.breaker.stats();
}
Expand Down
88 changes: 88 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,94 @@ describe('index', () => {
});
});

describe('updateCommitStatus', () => {
let config;
let apiUrl;
let fakeResponse;
let expectedOptions;

beforeEach(() => {
config = {
scmUri: 'bitbucket.org:batman/{1234}:mybranch',
sha: '40171b6785277ed1478ee2bc8587064e5a7d9fda',
buildStatus: 'SUCCESS',
token: 'sK6-nvoU%3D',
url: 'https://cd.screwdriver.cd/pipelines/1234',
jobName: 'main'
};
apiUrl = `${API_URL_V2}/repositories/batman/{1234}/commit/${config.sha}/statuses/build`;
fakeResponse = {
statusCode: 200
};
expectedOptions = {
url: apiUrl,
method: 'POST',
json: true,
body: {
url: config.url,
state: 'SUCCESSFUL',
key: config.sha,
description: 'Screwdriver/main'
},
auth: {
bearer: 'sK6-nvoU=' // Decoded access token
}
};
requestMock.yieldsAsync(null, fakeResponse);
});

it('successfully update status', () =>
scm.updateCommitStatus(config).then(() => {
assert.calledWith(requestMock, expectedOptions);
})
);

it('successfully update status with correct values', () => {
config.buildStatus = 'ABORTED';
delete config.jobName;

expectedOptions.body.state = 'STOPPED';
expectedOptions.body.description = 'Screwdriver';

return scm.updateCommitStatus(config).then(() => {
assert.calledWith(requestMock, expectedOptions);
});
});

it('rejects if status code is not 200', () => {
fakeResponse = {
statusCode: 401,
body: {
error: {
message: 'Access token expired'
}
}
};

requestMock.yieldsAsync(null, fakeResponse, fakeResponse.body);

return scm.updateCommitStatus(config).then(() => {
assert.fail('Should not get here');
}).catch((error) => {
assert.calledWith(requestMock, expectedOptions);
assert.match(error.message, 'STATUS CODE 401');
});
});

it('rejects if fails', () => {
const err = new Error('Bitbucket API error');

requestMock.yieldsAsync(err);

return scm.updateCommitStatus(config).then(() => {
assert.fail('Should not get here');
}).catch((error) => {
assert.calledWith(requestMock, expectedOptions);
assert.equal(error, err);
});
});
});

describe('stats', () => {
it('returns the correct stats', () => {
assert.deepEqual(scm.stats(), {
Expand Down

0 comments on commit 74d1eb0

Please sign in to comment.