Skip to content

Commit

Permalink
Merge pull request #560 from codestergit/bitbucket-inline
Browse files Browse the repository at this point in the history
Bitbucket Server Inline mode
  • Loading branch information
orta committed Apr 17, 2018
2 parents 9e564ee + 0beb915 commit def0595
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 59 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

* Fixed a bug where Danger posts empty main comment when it have one or more inline comments to post [@codestergit][]
* fix bug when commiting .png files on BitBucket [@Mifi][]
* Adds support for inline comments for bitbucket server. [@codestergit][]

## 3.4.7

Expand Down Expand Up @@ -1049,3 +1050,5 @@ Not usable for others, only stubs of classes etc. - [@orta][]
[@mxstbr]: https://github.com/mxstbr
[@happylinks]: https://github.com/happylinks
[@fwal]: https://github.com/fwal
[@codestergit]: https://github.com/codestergit

2 changes: 1 addition & 1 deletion source/danger.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ declare module "danger" {
interface BitBucketServerSegment {
lines: BitBucketServerLine[]
truncated: boolean
type: "ADDED" | "REMOVED"
type: "ADDED" | "REMOVED" | "CONTEXT"
}

interface BitBucketServerLine {
Expand Down
2 changes: 1 addition & 1 deletion source/dsl/BitBucketServerDSL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export interface BitBucketServerHunk {
export interface BitBucketServerSegment {
lines: BitBucketServerLine[]
truncated: boolean
type: "ADDED" | "REMOVED"
type: "ADDED" | "REMOVED" | "CONTEXT"
}

export interface BitBucketServerLine {
Expand Down
74 changes: 67 additions & 7 deletions source/platforms/BitBucketServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { BitBucketServerAPI } from "./bitbucket_server/BitBucketServerAPI"
import gitDSLForBitBucketServer from "./bitbucket_server/BitBucketServerGit"
import { Platform, Comment } from "./platform"

import * as debug from "debug"

/** Handles conforming to the Platform Interface for BitBucketServer, API work is handle by BitBucketServerAPI */

export class BitBucketServer implements Platform {
private readonly d = debug("danger:BitBucketServer")
name: string

constructor(public readonly api: BitBucketServerAPI) {
Expand All @@ -30,7 +33,7 @@ export class BitBucketServer implements Platform {
/**
* Gets inline comments for current PR
*/
getInlineComments = async (_: string): Promise<Comment[]> => new Promise<Comment[]>((_resolve, reject) => reject())
getInlineComments = async (dangerID: string): Promise<Comment[]> => this.api.getDangerInlineComments(dangerID)

/**
* Fails the current build, if status setting succeeds
Expand Down Expand Up @@ -91,7 +94,7 @@ export class BitBucketServer implements Platform {
}

supportsInlineComments() {
return false
return true
}

/**
Expand All @@ -108,25 +111,82 @@ export class BitBucketServer implements Platform {
*
* @returns {Promise<any>} JSON response of new comment
*/
createInlineComment = (_git: GitDSL, _comment: string, _path: string, _line: number): Promise<any> =>
new Promise((_resolve, reject) => reject())
createInlineComment = (git: GitDSL, comment: string, path: string, line: number): Promise<any> => {
if (!this.supportsInlineComments) {
return new Promise((_resolve, reject) => reject())
}
return this.findTypeOfLine(git, line, path).then(type => {
return this.api.postInlinePRComment(comment, line, type, path)
})
}

/**
* Finds type of line in given diff. This is needed for Bitbucket Server API
*
* @returns {Promise<string>} A string with type of line
*/
findTypeOfLine = (git: GitDSL, line: number, path: string): Promise<string> => {
return git.structuredDiffForFile(path).then(diff => {
return new Promise<string>((resolve, reject) => {
if (diff === undefined) {
this.d("Diff not found for inline comment." + path + "#" + line + ". Diff: " + JSON.stringify(diff))
reject()
}
let change
for (let chunk of diff!.chunks) {
// Search for a change (that is not a deletion) and with given line. We want to look only for destination lines of a change
change = chunk.changes.find((c: any) => c.type != "del" && c.destinationLine == line)
break
}
if (change === undefined) {
this.d("change not found for inline comment line " + path + "#" + line + ".")
reject()
} else {
resolve(change.type)
}
})
})
}

/**
* Updates an inline comment if possible. If platform can't update an inline comment,
* it returns a promise rejection. (e.g. platform doesn't support inline comments or line was out of diff).
*
* @returns {Promise<any>} JSON response of new comment
*/
updateInlineComment = (_comment: string, _commentId: string): Promise<any> =>
new Promise((_resolve, reject) => reject())
updateInlineComment = async (comment: string, commentId: string): Promise<any> => {
if (!this.supportsInlineComments) {
return new Promise((_resolve, reject) => reject())
}
const activities = await this.api.getPullRequestComments()
const updateComment = activities
.filter(activity => activity.commentAnchor)
.map(activity => activity.comment)
.filter(Boolean)
.find(comment => comment!.id.toString() == commentId)

return this.api.updateComment(updateComment!, comment)
}

/**
* Deletes an inline comment, used when you have
* fixed all your failures.
*
* @returns {Promise<boolean>} did it work?
*/
deleteInlineComment = async (_id: string): Promise<boolean> => new Promise<boolean>((_resolve, reject) => reject())
deleteInlineComment = async (id: string): Promise<any> => {
if (!this.supportsInlineComments) {
return new Promise<boolean>((_resolve, reject) => reject())
}
const activities = await this.api.getPullRequestComments()
const deleteComment = activities
.filter(activity => activity.commentAnchor)
.map(activity => activity.comment)
.filter(Boolean)
.find(comment => comment!.id.toString() == id)

return this.api.deleteComment(deleteComment!)
}

/**
* Deletes the main Danger comment, used when you have
Expand Down
57 changes: 55 additions & 2 deletions source/platforms/bitbucket_server/BitBucketServerAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
BitBucketServerDiff,
RepoMetaData,
} from "../../dsl/BitBucketServerDSL"
import { Comment } from "../platform"

import { Env } from "../../ci_source/ci_source"
import { dangerSignaturePostfix, dangerIDToString } from "../../runner/templates/bitbucketServerTemplate"
Expand Down Expand Up @@ -131,6 +132,30 @@ export class BitBucketServerAPI {
.filter(comment => v.includes(comment!.text, dangerSignaturePostfix))
}

getDangerInlineComments = async (dangerID: string): Promise<Comment[]> => {
const username = this.repoCredentials.username
const activities = await this.getPullRequestComments()
const dangerIDMessage = dangerIDToString(dangerID)

const comments = activities
.filter(activity => activity.commentAnchor)
.map(activity => activity.comment)
.filter(Boolean) as BitBucketServerPRComment[]
return new Promise<Comment[]>(resolve => {
resolve(
comments
.map((i: any) => {
return {
id: i.id,
ownedByDanger: i.author.name === username && i.text.includes(dangerIDMessage),
body: i.text,
}
})
.filter((i: any) => i.ownedByDanger)
)
})
}

getFileContents = async (filePath: string, repoSlug: string, refspec: string) => {
const path = `${repoSlug}/` + `raw/${filePath}` + `?at=${refspec}`
const res = await this.get(path, undefined, true)
Expand Down Expand Up @@ -162,6 +187,30 @@ export class BitBucketServerAPI {
return await res.json()
}

postInlinePRComment = async (comment: string, line: number, type: string, filePath: string) => {
const path = `${this.getPRBasePath()}/comments`
const t = { add: "ADDED", normal: "CONTEXT", del: "REMOVED" }[type]

const res = await this.post(
path,
{},
{
text: comment,
anchor: {
line: line,
lineType: t,
fileType: "TO",
path: filePath,
},
}
)
if (res.ok) {
return res.json()
} else {
throw await res.json()
}
}

deleteComment = async ({ id, version }: BitBucketServerPRComment) => {
const path = `${this.getPRBasePath()}/comments/${id}?version=${version}`
const res = await this.delete(path)
Expand All @@ -180,7 +229,11 @@ export class BitBucketServerAPI {
version,
}
)
return await res.json()
if (res.ok) {
return res.json()
} else {
throw await res.json()
}
}

// API implementation
Expand Down Expand Up @@ -209,7 +262,7 @@ export class BitBucketServerAPI {
}

get = (path: string, headers: any = {}, suppressErrors?: boolean): Promise<node_fetch.Response> =>
this.api(path, headers, undefined, "GET", suppressErrors)
this.api(path, headers, null, "GET", suppressErrors)

post = (path: string, headers: any = {}, body: any = {}, suppressErrors?: boolean): Promise<node_fetch.Response> =>
this.api(path, headers, JSON.stringify(body), "POST", suppressErrors)
Expand Down
37 changes: 23 additions & 14 deletions source/platforms/bitbucket_server/BitBucketServerGit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { BitBucketServerAPI } from "../bitbucket_server/BitBucketServerAPI"
import { GitJSONToGitDSLConfig, gitJSONToGitDSL, GitStructuredDiff } from "../git/gitJSONToGitDSL"

import * as debug from "debug"
import { EOL } from "os"
const d = debug("danger:BitBucketServerGit")

/**
Expand All @@ -37,15 +36,15 @@ function bitBucketServerCommitToGitCommit(
},
committer: bbsCommit.committer
? {
email: bbsCommit.committer.emailAddress,
name: bbsCommit.committer.name,
date: new Date(bbsCommit.committerTimestamp).toISOString(),
}
email: bbsCommit.committer.emailAddress,
name: bbsCommit.committer.name,
date: new Date(bbsCommit.committerTimestamp).toISOString(),
}
: {
email: bbsCommit.author.emailAddress,
name: bbsCommit.author.name,
date: new Date(bbsCommit.authorTimestamp).toISOString(),
},
email: bbsCommit.author.emailAddress,
name: bbsCommit.author.name,
date: new Date(bbsCommit.authorTimestamp).toISOString(),
},
message: bbsCommit.message,
tree: null,
url,
Expand Down Expand Up @@ -98,14 +97,24 @@ const bitBucketServerDiffToGitJSONDSL = (diffs: BitBucketServerDiff[], commits:
}

const bitBucketServerDiffToGitStructuredDiff = (diffs: BitBucketServerDiff[]): GitStructuredDiff => {
// We need all changed lines with it's type. It will convert hunk segment lines to flatten changed lines.
const segmentValues = { ADDED: "add", CONTEXT: "normal", REMOVED: "del" }
return diffs.map(diff => ({
from: diff.source && diff.source.toString,
to: diff.destination && diff.destination.toString,
chunks: diff.hunks && diff.hunks.map(hunk => ({
changes: hunk.segments.map(segment => ({
type: segment.type === "ADDED" ? ("add" as "add") : ("del" as "del"),
content: segment.lines.map(({ line }) => line).join(EOL),
chunks:
diff.hunks &&
diff.hunks.map(hunk => ({
changes: hunk.segments
.map(segment =>
segment.lines.map(line => ({
type: segmentValues[segment.type] as "add" | "del" | "normal",
content: line.line,
sourceLine: line.source,
destinationLine: line.destination,
}))
)
.reduce((a, b) => a.concat(b), []),
})),
})),
}))
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,81 @@ exports[`the dangerfile gitDSL - BitBucket Server should include \`after\` text

exports[`the dangerfile gitDSL - BitBucket Server should include \`before\` text content of the file 1`] = `"{\\"path\\":\\".gitignore\\",\\"repoSlug\\":\\"projects/PROJ/repos/repo\\",\\"ref\\":\\"d6725486c38d46a33e76f622cf24b9a388c8d13d\\"}"`;

exports[`the dangerfile gitDSL - BitBucket Server should include \`removed\` text content of the file 1`] = `"node_modules*.log/test.py/.vscode.DS_Storecoverage.idea__pycache__/*.pyc*.swp"`;
exports[`the dangerfile gitDSL - BitBucket Server should not include \`removed\` text content of the file 1`] = `""`;

exports[`the dangerfile gitDSL - BitBucket Server shows the diff for a specific file 1`] = `"node_modules*.log/test.py/.vscode.DS_Storecoverage.idea__pycache__/*.pyc*.swp"`;

exports[`the dangerfile gitDSL - BitBucket Server shows the structured diff for a specific file 1`] = `
Array [
Object {
"changes": Array [
Object {
"content": "node_modules",
"destinationLine": 1,
"sourceLine": 1,
"type": "normal",
},
Object {
"content": "*.log",
"destinationLine": 2,
"sourceLine": 2,
"type": "normal",
},
Object {
"content": "/test.py",
"destinationLine": 3,
"sourceLine": 3,
"type": "normal",
},
Object {
"content": "/.vscode",
"destinationLine": 4,
"sourceLine": 4,
"type": "normal",
},
Object {
"content": ".DS_Store",
"destinationLine": 5,
"sourceLine": 5,
"type": "normal",
},
Object {
"content": "coverage",
"destinationLine": 6,
"sourceLine": 6,
"type": "normal",
},
Object {
"content": ".idea",
"destinationLine": 7,
"sourceLine": 7,
"type": "normal",
},
Object {
"content": "__pycache__/",
"destinationLine": 8,
"sourceLine": 8,
"type": "normal",
},
Object {
"content": "*.pyc",
"destinationLine": 9,
"sourceLine": 9,
"type": "normal",
},
Object {
"content": "",
"destinationLine": 10,
"sourceLine": 10,
"type": "add",
},
Object {
"content": "*.swp",
"destinationLine": 11,
"sourceLine": 10,
"type": "normal",
},
],
},
]
`;

0 comments on commit def0595

Please sign in to comment.