Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a command to edit existing worklogs #46

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ export default {
})
},

async updateWorklog(worklogId: number, request: AddWorklogRequest): Promise<WorklogEntity> {
const credentials = await authenticator.getCredentials()
const body = { ...request, authorAccountId: credentials.accountId}
MiSikora marked this conversation as resolved.
Show resolved Hide resolved
return execute(async () => {
const response = await tempoAxios.put(`/worklogs/${worklogId}`, body)
debugLog(response)
return response.data
})
},

async getWorklogs(request: GetWorklogsRequest): Promise<GetWorklogsResponse> {
const credentials = await authenticator.getCredentials()
return execute(async () => {
Expand Down
67 changes: 67 additions & 0 deletions src/commands/edit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {Command, flags} from '@oclif/command'
import {appName} from '../appName'
import {trimIndent} from '../trimIndent'
import tempo from '../tempo'
import globalFlags from '../globalFlags'

export default class Edit extends Command {
static description = '[or e], edit a worklog entry with a given id)'

static examples = [
`${appName} edit 123456 -d "new desc"`,
`${appName} e 123456 -i ISSUE_KEY_OR_ALIAS`,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be rather something like ${appName} e 123456 -i abc-123 as in other examples.

`${appName} e 123456 -t 13-15:30 `
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add more examples (with remaining flags).

]

static aliases = ['e']

static flags = {
help: flags.help({char: 'h'}),
MiSikora marked this conversation as resolved.
Show resolved Hide resolved
debug: flags.boolean(),
description: flags.string({
char: 'd',
description: 'the new description for the worklog being edited'
}),
issue: flags.string({
char: 'i',
description: 'the issue or alias for an issue you want to move the worklog to'
}),
time: flags.string({
char: 't',
description: 'the new worklog duration (e.g 15m) or interval (e.g 11:30-14)'
}),
start: flags.string({
char: 's',
description: 'start time (HH:mm format), used when the input is a duration'
}),
'remaining-estimate': flags.string({
char: 'r',
description: 'remaining estimate'
}),
when: flags.string({
char: 'w',
description: 'change the date of the worklog'
})
}

static args = [
{
name: 'worklog_id',
description: 'worklog id to edit, like 123456',
required: true
}
]

async run() {
const {args, flags} = this.parse(Edit)
MiSikora marked this conversation as resolved.
Show resolved Hide resolved
globalFlags.debug = flags.debug
await tempo.updateUserWorklog(args.worklog_id, {
description: flags.description,
issueKeyOrAlias: flags.issue,
durationOrInterval: flags.time,
startTime: flags.start,
remainingEstimate: flags["remaining-estimate"],
when: flags.when
})
}
}
10 changes: 9 additions & 1 deletion src/tempo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import authenticator from './config/authenticator'
import worklogs, { AddWorklogInput } from './worklogs/worklogs'
import worklogs, {AddWorklogInput, UpdateWorklogInput} from './worklogs/worklogs'
import prompts from './config/prompts'
import * as worklogsTable from './worklogs/worklogsTable'
import chalk from 'chalk'
Expand Down Expand Up @@ -63,6 +63,14 @@ export default {
}
},

async updateUserWorklog(worklog_id: string, input: UpdateWorklogInput) {
return execute(async () => {
cli.action.start(`Updating worklog with id ${worklog_id}`)
const worklog = await worklogs.updateWorklog(worklog_id, input)
cli.action.stop('Done.')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about printing also worklog id here? (this value may have been changed which is not obvious)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it should, it seems like I forgot to add it.
Would the message be as in the addWorklog method, to include how to delete the, possibly changed, worklog?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the message be as in the addWorklog method, to include how to delete the, possibly changed, worklog?

Yes.

})
},

async listUserWorklogs(when?: string, verbose = false) {
execute(async () => {
cli.action.start('Loading worklogs')
Expand Down
60 changes: 53 additions & 7 deletions src/worklogs/worklogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ export type AddWorklogInput = {
remainingEstimate?: string
}

export type UpdateWorklogInput = {
issueKeyOrAlias?: string
durationOrInterval?: string
description?: string
startTime?: string,
remainingEstimate?: string,
when?: string
}

export type Worklog = {
id: string,
interval?: Interval,
Expand All @@ -44,13 +53,7 @@ export default {
async addWorklog(input: AddWorklogInput): Promise<Worklog> {
await checkToken()
const referenceDate = parseWhenArg(time.now(), input.when)
const parseResult = timeParser.parse(input.durationOrInterval, referenceDate)
if (parseResult == null) {
throw Error(`Error parsing "${input.durationOrInterval}". Try something like 1h10m or 11-12:30. See ${appName} log --help for more examples.`)
}
if (parseResult.seconds <= 0) {
throw Error('Error. Minutes worked must be larger than 0.')
}
const parseResult = parseInputDate(input.durationOrInterval, referenceDate)
const issueKey = await aliases.getIssueKey(input.issueKeyOrAlias) ?? input.issueKeyOrAlias
const worklogEntity = await api.addWorklog({
issueKey: issueKey,
Expand Down Expand Up @@ -95,7 +98,50 @@ export default {
credentials.accountId
)
return { worklogs, date, scheduleDetails }
},

async updateWorklog(worklogIdInput: string, input: UpdateWorklogInput) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worklogIdInput should be the part of UpdateWorklogInput type.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, you should call await checkToken() to check if token is properly set. See the other methods in this scope.

const worklogId = parseInt(worklogIdInput)
if (!Number.isInteger(worklogId)) {
throw Error('Error. Worklog id should be an integer number.')
}
const currentWorklog = await api.getWorklog(worklogId)
const referenceDate = parseWhenArg(time.now(), input.when ? input.when : currentWorklog.startDate);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input.when ? input.when : currentWorklog.startDate
can be replaced with
input.when ?? currentWorklog.startDate

let parseResult = undefined
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to declare variable as undefined explicitly.

if(input.durationOrInterval) {
MiSikora marked this conversation as resolved.
Show resolved Hide resolved
parseResult = parseInputDate(input.durationOrInterval, referenceDate)
}

const issueKey = input.issueKeyOrAlias ? input.issueKeyOrAlias : currentWorklog.issue.key;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, I would use ?? here.

const issue = await aliases.getIssueKey(issueKey) ?? issueKey
Comment on lines +115 to +116
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO issueKey should be named issueKeyOrAlias, and issue should be named issueKey.

let worklogEntity;
const request = {
issueKey: issue,
timeSpentSeconds: parseResult ? parseResult.seconds : currentWorklog.timeSpentSeconds,
startDate: format(referenceDate, DATE_FORMAT),
startTime: parseResult ? startTime(parseResult, input.startTime, referenceDate) : currentWorklog.startTime,
description: input.description ? input.description : currentWorklog.description,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above (using ??)

remainingEstimateSeconds: remainingEstimateSeconds(referenceDate, input.remainingEstimate)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we don't use remaining estimate from current worklog?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the remaining estimate seems to be optional, which would mean not setting it results in it not being changed.
But it could be done explicitly to make it clearer, i'll see how it behaves now.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the remaining estimate seems to be optional, which would mean not setting it results in it not being changed.

But technically if the issueKey was changed, we will create a completely new worklog, so we have to move all things from the old to the new one, including optional parameters.

Correct me if I am wrong.

};
if(input.issueKeyOrAlias) {
MiSikora marked this conversation as resolved.
Show resolved Hide resolved
await this.deleteWorklog(worklogIdInput);
worklogEntity = await api.addWorklog(request);
} else {
Comment on lines +126 to +129
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not transactional. What if a worklog removal was done successfully but adding a new worklog failed (for example a new issue key is not found)? Then we just have removed worklog, not updated.

I am curious how it's implemented in Jira UI 🤔
Maybe it would be safer to firstly add a new worklog and make deletion after successful adding? I am not sure, think about it, current behavior isn't acceptable unfortunately IMO.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right.
If we add the new worklog first and there is an error, this should cancel, i'll check how this behaves by default.
If after adding the new worklog the deletion fails for some reason, it shouldn't matter too much, just need to inform the user.

worklogEntity = await api.updateWorklog(worklogId, request)
}
return toWorklog(worklogEntity)
}
}

function parseInputDate(durationOrInterval: string, referenceDate: Date) : ParseResult {
const parseResult = timeParser.parse(durationOrInterval, referenceDate);
if (parseResult == null) {
throw Error(`Error parsing "${durationOrInterval}". Try something like 1h10m or 11-12:30. See ${appName} log --help for more examples.`)
}
if (parseResult.seconds <= 0) {
throw Error('Error. Minutes worked must be larger than 0.')
}
return parseResult;
}

function remainingEstimateSeconds(referenceDate: Date, remainingEstimate?: string): number | undefined {
Expand Down