-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel.js
204 lines (181 loc) · 6.24 KB
/
model.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/**
* A git Branch object
*/
class Branch {
constructor(name) {
this.name = name
}
}
/**
* A PullRequest object with the base and the head Branches
*/
class PullRequest {
/**
* Construct a PullRequest passing the head and base Branches
* @param {Branch} head The head branch
* @param {Branch} base The base branch
*/
constructor(head, base) {
this.head = head
this.base = base
}
toString() {
return `PR: ${this.head} into ${this.base}`
}
}
/**
* A TreeNode representation.
* A node is a simple object which holds a data and may be linked to at most one parent node and may have {0,N} children.
*/
class TreeNode {
/**
* Construct a TreeNode passing the data it will hold.
* @param data The data the node will hold. Data MUST not be null
*/
constructor(data) {
if (data == null) {
console.error("Trying to initialise a TreeNode with a data null")
process.exit()
}
this.data = data
this.children = []
this.parent = null
}
toString() {
return this.data
}
}
/**
* A Tree is an object which holds a node also called rootNode.
*/
class Tree {
/**
* Initialise a Tree with a node. The root node MUST not have any parent node.
* @param {TreeNode} rootNode The rootNode
*/
constructor(rootNode) {
if (rootNode.parent != null) {
console.error("Trying to initialise a Tree with a rootNode whose parent isn't null")
process.exit();
}
this.rootNode = rootNode
}
/**
* The visual representation of the Tree in a string format
*/
toString() {
const rootNodeString = this.rootNode.toString()
const childrenString = this.nodeString(this.rootNode, 0, true)
return "Tree:\n\n" + rootNodeString + childrenString
}
nodeString(node, currentHeight, isRootNodeCaller) {
// Generate the Tree output
let childrenString = ""
const lastIndex = node.children.length - 1
node.children.forEach((child, index) => {
childrenString += "\n"
if (currentHeight > 0 && !isRootNodeCaller) {
childrenString += "│"
}
childrenString += "\t".repeat(currentHeight)
if (index < lastIndex) {
childrenString += "├────"
childrenString += child.toString()
childrenString += this.nodeString(child, currentHeight + 1, false)
} else {
childrenString += "└────"
childrenString += child.toString()
childrenString += this.nodeString(child, currentHeight + 1, isRootNodeCaller)
if (currentHeight == 0 || isRootNodeCaller) {
childrenString += "\n"
} else {
childrenString += "\n│"
}
}
})
return childrenString
}
/**
* Search a node containing the data passed as argument using the depth first search algorythm
*/
depthFirstSearch(data) {
return this.dfs(data, this.rootNode)
}
dfs(data, rootNode) {
// If the current node's data is equal to the required data, return it
for (const currentChildNode of rootNode.children) {
if (currentChildNode.data == data) {
return currentChildNode
// Otherwise search recursively the data scanning its children first
} else if (currentChildNode.children.length != 0) {
const result = this.dfs(data, currentChildNode)
if (result != null) {
return result
}
// If none of the children holds the required data, go to the next node
} else {
continue
}
}
}
}
/**
* An helper class which builds a tree of PRs given a list of PRs and the default branch.
* When a PR is dependent by another PR, its parent will be equal to the depending PR.
* In the same way, the depending PR will have the dependent PR as one of the children
*/
class TreeBuilder {
/**
* Initialise the TreeBuilder passing a list or PRs and the name of the repo's default branch
*/
constructor(prs, defaultBranchName) {
// Turns the PRs into TreeNodes
const nodePRs = prs.map((pr) => {
const base = new TreeNode(pr.base.name)
const head = new TreeNode(pr.head.name)
// (base)
// /
// (head)
base.children.push(head)
return base
})
this.nodePRs = nodePRs
this.rootNode = new TreeNode(defaultBranchName)
}
/**
* Build a tree with the given defaultBranchName and PRs
* @return {Tree} The built tree
*/
generate() {
// Here's where the script goes thtough all the PRs and it generates the tree
this.findDependentPRs(this.rootNode, this.nodePRs)
return new Tree(this.rootNode)
}
findDependentPRs(topNode, pendingPRNodes) {
// It starts looking for all the PRs whose base is the the default branch
pendingPRNodes.slice().reverse().forEach( (node, index, _) => {
if (node.data == topNode.data) {
// When it finds a node whose base is the default branch take the children
node.children.forEach ( (subnode) => {
// Append the child to the list of the children
topNode.children.push(subnode)
subnode.parent = topNode
// Remove the following PR from the list of all the PRs
const notReversedIndex = pendingPRNodes.length - index - 1
let pendingPRNodesCopy = [...pendingPRNodes]
pendingPRNodesCopy.splice(notReversedIndex,1)
// Recursively call the same function for the child.
// Then, it will find all the PRs whose base branch is the same of the subnode
this.findDependentPRs(subnode, pendingPRNodesCopy)
})
}
})
}
}
module.exports = {
TreeBuilder,
Branch,
PullRequest,
TreeNode,
Tree
}