@beauraines/node-helpers 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,16 +2,27 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [2.6.0](https://github.com/beauraines/node-helpers/compare/v2.5.2...v2.6.0) (2023-06-14)
6
+
7
+
8
+ ### Features
9
+
10
+ * Adds Azure DevOps helpers ([#36](https://github.com/beauraines/node-helpers/issues/36)) ([3bf0658](https://github.com/beauraines/node-helpers/commit/3bf065851e2ebbeb17de4d010a8e5e25323f8ecf))
11
+
12
+ ### [2.5.2](https://github.com/beauraines/node-helpers/compare/v2.5.0...v2.5.2) (2023-06-03)
13
+
5
14
  ## [2.5.0](https://github.com/beauraines/node-helpers/compare/v2.4.3...v2.5.0) (2023-06-03)
6
15
 
7
16
 
8
17
  ### Features
9
18
 
10
19
  * new config module ([#32](https://github.com/beauraines/node-helpers/issues/32)) ([514df50](https://github.com/beauraines/node-helpers/commit/514df509ca33527a8b18e5efe43d1b772963e879))
20
+ * supports cross OS file paths ([#33](https://github.com/beauraines/node-helpers/issues/33)) ([27f5e27](https://github.com/beauraines/node-helpers/commit/27f5e27d02408bff07220c3e82bd90186023b324)), closes [#24](https://github.com/beauraines/node-helpers/issues/24)
11
21
 
12
22
 
13
23
  ### Bug Fixes
14
24
 
25
+ * adds export of new config module ([#34](https://github.com/beauraines/node-helpers/issues/34)) ([81c7a27](https://github.com/beauraines/node-helpers/commit/81c7a27696da3d9a0b6fb3a215418185c9b1d153))
15
26
  * **deps:** bump @azure/storage-queue from 12.12.0 to 12.13.0 ([45acd3d](https://github.com/beauraines/node-helpers/commit/45acd3d808ab064c1dbc4b492a013c7b60ebf5b2))
16
27
  * **deps:** bump dayjs from 1.11.7 to 1.11.8 ([2208985](https://github.com/beauraines/node-helpers/commit/22089855cd90758273bac28fa1d8669d21f661b5))
17
28
  * **deps:** bump node-fetch from 2.6.9 to 2.6.11 ([d9fbed2](https://github.com/beauraines/node-helpers/commit/d9fbed2e6ade418caada9cc68f6ac05ba4d3159d))
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
 
2
2
  const AzureStorage = require("./src/azure")
3
+ const config = require('./src/config.js')
3
4
  const credentials = require("./src/credentials.js");
4
5
  const database = require("./src/database");
5
6
  const helpers = require("./src/helpers");
@@ -7,6 +8,7 @@ const jira = require("./src/jira");
7
8
 
8
9
  module.exports = {
9
10
  AzureStorage,
11
+ config,
10
12
  credentials,
11
13
  database,
12
14
  helpers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beauraines/node-helpers",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "Collection of node helpers",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -12,6 +12,7 @@
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
14
  "@azure/storage-queue": "^12.11.0",
15
+ "azure-devops-node-api": "^12.0.0",
15
16
  "azure-storage": "^2.10.7",
16
17
  "dayjs": "^1.11.7",
17
18
  "node-fetch": "^2.6.7",
package/src/ado.js ADDED
@@ -0,0 +1,114 @@
1
+ const azdev = require('azure-devops-node-api')
2
+ const {getResourceId} = require('./helpers.js')
3
+ const {readConfig } = require('./config.js')
4
+ const fetch = require('node-fetch');
5
+
6
+ // get Current iteration
7
+ // Get current iteration work items
8
+ // get Teams
9
+ // get workitems
10
+ // update work items
11
+
12
+ async function getAdoApi(configFile) {
13
+ const config = await readConfig(configFile)
14
+
15
+ const orgUrl = config.org;
16
+ // let token: string = process.env.AZURE_PERSONAL_ACCESS_TOKEN;
17
+ const token= config.token
18
+ const teamContext = { project: config.project, team: config.team};
19
+
20
+ const authHandler = azdev.getPersonalAccessTokenHandler(token);
21
+ const connection = new azdev.WebApi(orgUrl, authHandler);
22
+
23
+ const workAPI = await connection.getWorkApi();
24
+
25
+ const workItemAPI = await connection.getWorkItemTrackingApi();
26
+
27
+ return {
28
+ workAPI,
29
+ workItemAPI,
30
+ teamContext,
31
+ config
32
+ }
33
+ }
34
+
35
+ async function getChildWorkItems(workItemAPI, id) {
36
+ let parentWorkItem = await workItemAPI.getWorkItem(id, undefined, undefined, 'Relations');
37
+
38
+
39
+ const childWorkItemIds = parentWorkItem.relations.filter(x => x.attributes.name == 'Child').map((x) => {
40
+ return getResourceId(x.url)
41
+ });
42
+
43
+ let childWorkItems = childWorkItemIds.map(id => workItemAPI.getWorkItem(id, undefined, undefined));
44
+ childWorkItems = await Promise.all(childWorkItems);
45
+
46
+ return childWorkItems
47
+ }
48
+
49
+ /**
50
+ * Gets array of distinct parent items when passed an array of work-item IDs
51
+ *
52
+ * @param {WorkItemAPI} workItemAPI
53
+ * @param {Array} ids The array of work-items of which to find the distinct parents
54
+ * @returns WorkItems[] Array of distinct parent work itesm
55
+ */
56
+ async function getDistinctParentWorkItems(workItemAPI, ids) {
57
+ let workItems = ids.map(id => {
58
+ return workItemAPI.getWorkItem(id, undefined, undefined, 'Relations')
59
+ })
60
+ workItems = await Promise.all(workItems)
61
+
62
+ let parentIds = workItems.map(wi => {
63
+ const parent = wi?.relations?.filter(x => x.attributes.name == 'Parent')[0]
64
+ return parent ? getResourceId(parent.url) : null
65
+ })
66
+
67
+ let parentWorkItems = parentIds.map(parentId => workItemAPI.getWorkItem(parentId,undefined, undefined, 'Relations'))
68
+ parentWorkItems = await Promise.all(parentWorkItems)
69
+ parentWorkItems = parentWorkItems.filter(x => x!=null)
70
+
71
+ let distinctParentWorkItemIds = [...new Set(parentWorkItems.map(wi => wi?.id))];
72
+
73
+ let distinctParentWorkItems = distinctParentWorkItemIds.map(distinctParentId => workItemAPI.getWorkItem(distinctParentId,undefined, undefined, 'Relations'))
74
+ distinctParentWorkItems = await Promise.all(distinctParentWorkItems)
75
+
76
+ return distinctParentWorkItems
77
+ }
78
+
79
+
80
+ // TODO do something different about how to Auth
81
+ async function callRestApi(url, username, token) {
82
+ // console.log(url) // Only display this in verbose mode
83
+ // Bearer token format for ADO
84
+ // eslint-disable-next-line no-undef
85
+ let bearerToken = Buffer.from(`${username}:${token}`).toString('base64');
86
+
87
+ let response;
88
+ try {
89
+ response = await fetch(url, {
90
+ method: 'GET',
91
+ headers: {
92
+ 'Authorization': `Basic ${bearerToken}`,
93
+ 'Accept': 'application/json',
94
+ 'Content-Type': 'application/json',
95
+ }
96
+ });
97
+
98
+ if (response.ok) {
99
+ return response.json();
100
+ } else {
101
+ throw new Error(response.statusText);
102
+ }
103
+ } catch (error) {
104
+ console.error(error.message)
105
+ }
106
+ }
107
+
108
+
109
+ module.exports = {
110
+ getAdoApi,
111
+ getChildWorkItems,
112
+ getDistinctParentWorkItems,
113
+ callRestApi
114
+ }
package/src/database.js CHANGED
@@ -2,15 +2,15 @@ const {homedir} = require('os');
2
2
  const sqlite = require('sqlite');
3
3
  const sqlite3 = require('sqlite3');
4
4
  const {fileExists} = require('./helpers');
5
+ const path = require('path');
5
6
 
6
7
  /**
7
8
  * Opens the BurnDownStatus SQLite3 Database
8
- * @param file file name of the SQLite3 DB. If not provided, defaults to ${homedir}/BurnDownStatus.db
9
+ * @param file file name of the SQLite3 DB. If not provided, defaults to BurnDownStatus.db in the users home
9
10
  * @returns SQLite database connection
10
11
  */
11
12
  async function getDBConnection(file) {
12
- const homeDir = homedir();
13
- file = file ? file : `${homeDir}/BurnDownStatus.db`;
13
+ file = file ? file : path.join(homedir(),'BurnDownStatus.db');
14
14
  if (! await fileExists(file)){
15
15
  console.error(`${file} not found`);
16
16
  // ! Separation of concerns - this should probably not be doing the exiting, but it is.
@@ -3,6 +3,7 @@ const sqlite = require('sqlite');
3
3
  const sqlite3 = require('sqlite3');
4
4
  const helpers = require('./helpers');
5
5
  const { getDBConnection } = require('./database');
6
+ const path = require('path');
6
7
 
7
8
  jest.mock('sqlite');
8
9
  jest.mock('os');
@@ -32,7 +33,7 @@ describe('database module', () => {
32
33
  helpers.fileExists.mockReturnValue(true);
33
34
 
34
35
 
35
- const expectedDefaultFile = `${os.homedir()}/BurnDownStatus.db`
36
+ const expectedDefaultFile = path.join(os.homedir(),'BurnDownStatus.db')
36
37
 
37
38
  const file = undefined;
38
39
  // call function with null file
@@ -56,7 +57,7 @@ describe('database module', () => {
56
57
  os.homedir.mockReturnValue(expectedHomeDir);
57
58
  helpers.fileExists.mockReturnValue(true);
58
59
 
59
- const expectedDefaultFile = `${os.homedir()}/my_database.db`
60
+ const expectedDefaultFile = path.join(os.homedir(),'BurnDownStatus.db')
60
61
 
61
62
  const db = await getDBConnection(expectedDefaultFile)
62
63
 
package/src/helpers.js CHANGED
@@ -121,8 +121,26 @@ function sparkline(data,label,options) {
121
121
  return `${label} [${minValue},${maxValue}] ${sparkly(data.map( x=> x- minValue))} ${lastValue}`
122
122
  }
123
123
 
124
+ /**
125
+ * Given a RESTful url e.g. https://www.example.com/app/#list/44719910/959889147?id=12234&363636=334'
126
+ * this function will return the resource ID, ignoring the query parameters
127
+ *
128
+ * @param {string} url The URL to find the resource or last part of the routing
129
+ * @returns string
130
+ */
131
+ const getResourceId = (url) => {
132
+ let queryStringStart = url.lastIndexOf('?')
133
+ if (queryStringStart == -1) {
134
+ queryStringStart = url.length
135
+ }
136
+ const lastSlash = url.lastIndexOf('/')
137
+ const id = url.substring(lastSlash+1,queryStringStart)
138
+ return id
139
+ }
140
+
124
141
  module.exports = {
125
142
  getEpochMillis,
143
+ getResourceId,
126
144
  fileExists,
127
145
  groupAndSum,
128
146
  readFile,