@@ -15,6 +15,7 @@ const TABLE_MERGE_REQUESTS = 'merge_requests';
1515const TABLE_TIMES = 'times' ;
1616const TABLE_LABELS = 'labels' ;
1717const TABLE_LABEL_GROUP = 'label_group' ;
18+ const TABLE_MILESTONES = 'milestones' ;
1819
1920const COLUMNS = {
2021 [ TABLE_ISSUES ] : 'issueColumns' ,
@@ -55,6 +56,10 @@ function getGeneralColumnType(columnName) {
5556 return `REFERENCES ${ TABLE_LABELS } (id)` ;
5657 }
5758
59+ if ( columnName === 'milestone' ) {
60+ return `REFERENCES ${ TABLE_MILESTONES } (id)` ;
61+ }
62+
5863 // default:
5964 return 'TEXT' ;
6065}
@@ -185,23 +190,49 @@ class Sqlite extends Base {
185190 super ( config , report ) ;
186191 this . tables = new Map ( ) ;
187192 this . stats = new Map ( ) ;
188- this . labels = new Map ( ) ;
189- this . labelGroupSerial = 0 ;
190- this . labelsSerial = 0 ;
191- this . makeDefaultTables ( ) ;
193+
194+ this . initSpecialTables ( ) ;
192195 }
193196
194- makeDefaultTables ( ) {
195- this . tables . set ( TABLE_LABELS , new Table (
196- TABLE_LABELS ,
197- [ 'id' , 'name' ] ,
198- [ ]
199- ) ) ;
200- this . tables . set ( TABLE_LABEL_GROUP , new Table (
201- TABLE_LABEL_GROUP ,
202- [ 'gid' , 'label_id' ] ,
203- [ ]
204- ) ) ;
197+ /**
198+ * Initializes special tables like labels or milestones, which are extracted from the output tables.
199+ */
200+ initSpecialTables ( ) {
201+ const issueColumns = this . config . get ( COLUMNS [ TABLE_ISSUES ] ) ;
202+ const mergeRequestColumns = this . config . get ( COLUMNS [ TABLE_MERGE_REQUESTS ] ) ;
203+ if ( issueColumns . includes ( 'labels' ) || mergeRequestColumns . includes ( 'labels' ) ) {
204+ /**
205+ * @type {Map<string, number> } Maps Labels to label group ids.
206+ */
207+ this . labels = new Map ( ) ;
208+ this . labelGroupSerial = 0 ;
209+ this . labelsSerial = 0 ;
210+
211+ this . tables . set ( TABLE_LABELS , new Table (
212+ TABLE_LABELS ,
213+ [ 'id' , 'name' ] ,
214+ [ ]
215+ ) ) ;
216+ this . tables . set ( TABLE_LABEL_GROUP , new Table (
217+ TABLE_LABEL_GROUP ,
218+ [ 'gid' , 'label_id' ] ,
219+ [ ]
220+ ) ) ;
221+ }
222+
223+ if ( issueColumns . includes ( 'milestone' ) || mergeRequestColumns . includes ( 'milestone' ) ) {
224+ /**
225+ * @type {Map<string, number> } Maps Milestones to ids
226+ */
227+ this . milestones = new Map ( ) ;
228+ this . milestonesSerial = 0 ;
229+
230+ this . tables . set ( TABLE_MILESTONES , new Table (
231+ TABLE_MILESTONES ,
232+ [ 'id' , 'name' ] ,
233+ [ ]
234+ ) ) ;
235+ }
205236 }
206237
207238 makeStats ( ) {
@@ -227,6 +258,13 @@ class Sqlite extends Base {
227258 const table = this . makeTable ( TABLE_ISSUES , this . report . issues ) ;
228259 const columns = this . config . get ( COLUMNS [ TABLE_ISSUES ] ) ;
229260
261+ if ( columns . includes ( 'milestone' ) ) {
262+ table . records . forEach ( record => {
263+ const columnIndex = columns . indexOf ( 'milestone' ) ;
264+ record [ columnIndex ] = this . parseMilestone ( record [ columnIndex ] ) ;
265+ } ) ;
266+ }
267+
230268 if ( columns . includes ( 'labels' ) ) {
231269 columns . push ( 'label_group' ) ;
232270
@@ -262,24 +300,38 @@ class Sqlite extends Base {
262300 return table ;
263301 }
264302
303+ /**
304+ * Creates a normalized table for milestones.
305+ *
306+ * @param milestone {string | number} Name of the milestone (or 0 if empty)
307+ * @returns {number | null } ID of the milestone record.
308+ */
309+ parseMilestone ( milestone ) {
310+ if ( milestone === 0 ) {
311+ return null ;
312+ }
313+
314+ return this . getOrCreateMilestoneId ( milestone ) ;
315+ }
316+
265317
266318 /**
267319 * Creates a normalized labels structure from a labels comma list.
268320 *
269321 * @param labels {Array<string>}
270- * @returns {number | 'NULL' } id of the new label group
322+ * @returns {number | null } id of the new label group
271323 */
272324 parseLabelList ( labels ) {
273325 const serial = this . labelGroupSerial ++ ;
274326
275327 if ( ! labels ) {
276- return 'NULL' ;
328+ return null ;
277329 }
278330
279331 const records = labels
280332 . map ( label => this . getOrCreateLabelId ( label ) )
281333 . map ( labelId => [ serial , labelId ] ) ;
282- this . tables . get ( 'label_group' ) . records . push ( ...records ) ;
334+ this . tables . get ( TABLE_LABEL_GROUP ) . records . push ( ...records ) ;
283335
284336 return serial ;
285337 }
@@ -302,6 +354,24 @@ class Sqlite extends Base {
302354 }
303355
304356
357+ /**
358+ * Creates a Milestone (or fetches the existing one) and returns the ID.
359+ *
360+ * @param milestone {string}
361+ * @returns {number }
362+ */
363+ getOrCreateMilestoneId ( milestone ) {
364+ if ( this . milestones . has ( milestone ) ) {
365+ return this . milestones . get ( milestone ) ;
366+ }
367+
368+ const serial = this . milestonesSerial ++ ;
369+ this . tables . get ( 'milestones' ) . records . push ( [ serial , milestone ] ) ;
370+ this . milestones . set ( milestone , serial ) ;
371+ return serial ;
372+ }
373+
374+
305375 toFile ( file , resolve ) {
306376 this . db = new SqliteDatabaseAbstraction ( file ) ;
307377 this . provisionDatabase ( )
0 commit comments