forked from altayer-digital/toggl-tempo-sync
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
executable file
·146 lines (131 loc) · 3.8 KB
/
index.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
'use strict'
const _get = require('lodash.get')
/**
* @var {ConfigResponse} config
*/
const config = require('./config')
const {
formatDate,
formatTime
} = require('./helpers/date')
const {
getUniqueEntries,
parseJiraData
} = require('./helpers/entry')
const {
createTempoClient,
createTogglClient
} = require('./services/client.js')
const { queryTogglEntries } = require('./services/togglEntries')
const { queryTempoEntries } = require('./services/tempoEntries')
/**
* Main logic for toggl to tempo entry creation
*
* @param {date8601} from
* @param {date8601} to
* @param {string} utc
* @param {boolean|string} dryRun
* @return {Promise<void>}
*/
const transferFromTogglToTempo = async (from, to, utc, dryRun = false) => {
const toggleClient = createTogglClient()
let timeEntries = await queryTogglEntries(toggleClient, from, to, utc)
console.log('number of time entries from toggl', timeEntries.length)
if (config.compact.all) {
timeEntries = getUniqueEntries(timeEntries)
console.log('number of unique entries', timeEntries.length)
}
const parsedEntries = timeEntries
.map((timeEntry) => parseJiraData(timeEntry))
.filter(({ issueKey }) => Boolean(issueKey))
if (dryRun) return
const tempoClient = createTempoClient()
// create worklogs in tempo from toggl time entries
await Promise.all(
parsedEntries.map(
async ({
issueKey,
start,
duration,
comment
}) => {
return tempoClient.post('worklogs', {
authorAccountId: config.JiraAccountId,
issueKey,
startDate: formatDate(start),
startTime: formatTime(start),
timeSpentSeconds: duration,
billableSeconds: duration,
description: comment
}).catch(e => printError(e))
}
)
)
console.log('number of worklogs added to tempo', parsedEntries.length)
}
/**
* Main logic for deleting Tempo entries
*
* @param {date8601} from
* @param {date8601} to
* @param {boolean|string} dryRun
* @return {Promise<void>}
* @throws {Error}
*/
const removeFromTempo = async (from, to, dryRun = false) => {
const entries = await queryTempoEntries(createTempoClient(), from, to)
if (dryRun) {
console.log(`Would be removing ${entries.length} entries`)
console.log('first entry', _get(entries, '[0]'))
console.log('last entry', _get(entries, `[${entries.length - 1}]`))
return
}
const tempoClient = createTempoClient()
await Promise.all(
entries.map(
async ({ tempoWorklogId }) => {
return tempoClient.delete(`worklogs/${tempoWorklogId}`)
}
)
)
console.log(`Finished removing ${entries.length} entries`)
}
const fromDate = formatDate(config.from ? config.from : config._[0])
const configToDate = config.to ? formatDate(config.to) : config.to
const toDate = configToDate || (config._[1] ? formatDate(config._[1]) : fromDate)
if (config.delete) {
removeFromTempo(fromDate, toDate, config.dryRun)
.catch(error => printError(error))
} else {
transferFromTogglToTempo(fromDate, toDate, config.utc, config.dryRun)
.catch(error => printError(error))
}
/**
* Helps print errors
* @param {AxiosError} error
* @throws {Error}
*/
const printError = error => {
if (!error.response) {
console.log(error)
throw Error()
}
console.log(translateError(error))
throw Error(error.message)
}
/**
* Structures error
* @param {AxiosError} error
* @return {string}
*/
const translateError = error => {
const errorList = _get(error, 'response.data.errors', []).map(err => err.message)
return JSON.stringify({
url: `${error.config.method.toUpperCase()} ${error.config.baseURL}${error.config.url}`,
status: error.response.status,
statusText: error.response.statusText,
message: error.message,
errorList,
requestBody: error.config.data
})
}