👩💻 feat: save new time entry
This commit is contained in:
parent
74c11dc53c
commit
71b66181d4
10
src/Entities/Interfaces/TimeEntryInterface.ts
Normal file
10
src/Entities/Interfaces/TimeEntryInterface.ts
Normal file
@ -0,0 +1,10 @@
|
||||
interface TimeEntryInterface {
|
||||
id?: number,
|
||||
projectId: number,
|
||||
taskId: number,
|
||||
date: string,
|
||||
notes: string,
|
||||
isRunning?: boolean
|
||||
}
|
||||
|
||||
export default TimeEntryInterface
|
@ -37,4 +37,6 @@ class ProjectCollection {
|
||||
})
|
||||
return project
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ProjectCollection
|
||||
|
@ -1,44 +1,84 @@
|
||||
import * as vscode from "vscode";
|
||||
import Harvest from "../../Entities/Harvest"
|
||||
import UserInterface from "../../Entities/Interfaces/UserInterface";
|
||||
import User from "../../Entities/User"
|
||||
import getUser from "../getUser";
|
||||
import TaskInterface from "../../Entities/Interfaces/TaskInterface";
|
||||
import TimeEntryInterface from "../../Entities/Interfaces/TimeEntryInterface";
|
||||
import Project from "../../Entities/Project";
|
||||
import ProjectCollection from "../../Entities/ProjectCollection";
|
||||
import saveNewTimeEntry from "../saveNewTimeEntry";
|
||||
import SetupHarvest from "../SetupHarvest"
|
||||
|
||||
|
||||
function PunchTime (context: vscode.ExtensionContext): vscode.Disposable {
|
||||
return vscode.commands.registerCommand('harvest-vscode.punchTime', async () => {
|
||||
const harvest = new Harvest()
|
||||
let user = new User()
|
||||
|
||||
const accountId: string = context.globalState.get('accountId') || ''
|
||||
const accessToken: string = context.globalState.get('accessToken') || ''
|
||||
|
||||
if (!accountId || !accessToken) {
|
||||
vscode.window.showErrorMessage('Run "Harvest: Login" Command before trying to puch time')
|
||||
let projectCollection = new ProjectCollection()
|
||||
const isHarvestSetup = await SetupHarvest(context)
|
||||
if (!isHarvestSetup) return
|
||||
|
||||
const projectNames = projectCollection.elements.map((p: Project) => {
|
||||
return p.name
|
||||
})
|
||||
|
||||
const selectedProjectName = await vscode.window.showQuickPick(projectNames, {
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: 'Choost a Project'
|
||||
})
|
||||
|
||||
if (!selectedProjectName) {
|
||||
vscode.window.showWarningMessage('Must select a project to push time')
|
||||
return
|
||||
}
|
||||
|
||||
harvest.accountId = accountId
|
||||
harvest.accessToken = accessToken
|
||||
const selectedProject = projectCollection.findByName(selectedProjectName)
|
||||
|
||||
if (!user.id) {
|
||||
let userProps: UserInterface
|
||||
try {
|
||||
userProps = await getUser()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
vscode.window.showErrorMessage('Could not retrieve user data from Harvest')
|
||||
return
|
||||
}
|
||||
if (!userProps.id) {
|
||||
vscode.window.showErrorMessage('Could not retrieve user data from Harvest')
|
||||
return
|
||||
}
|
||||
user.destructor()
|
||||
user = new User(userProps)
|
||||
vscode.window.showInformationMessage('Successfully authenticated with Harvest')
|
||||
const taskNames = selectedProject?.tasks.map((t: TaskInterface) => {
|
||||
return t.name
|
||||
})
|
||||
|
||||
if (!taskNames) {
|
||||
vscode.window.showWarningMessage('No tasks defined for this project.')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(user)
|
||||
const selectedTaskName = await vscode.window.showQuickPick(taskNames, {
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: 'Choost a Task'
|
||||
})
|
||||
|
||||
if (!selectedTaskName) {
|
||||
vscode.window.showWarningMessage('Must select a task to push time')
|
||||
return
|
||||
}
|
||||
|
||||
const selectedTask = selectedProject?.tasks.find((t: TaskInterface) => {
|
||||
return t.name === selectedTaskName
|
||||
})
|
||||
|
||||
// console.log(selectedProject?.tasks[0])
|
||||
// console.log(selectedProject?.tasks[1])
|
||||
// console.log(selectedProject?.tasks[2])
|
||||
console.log(selectedTask)
|
||||
|
||||
const notes = await vscode.window.showInputBox({
|
||||
ignoreFocusOut: true,
|
||||
placeHolder: 'Notes'
|
||||
})
|
||||
|
||||
if (!notes) {
|
||||
vscode.window.showWarningMessage('Must add notes to puch time.')
|
||||
return
|
||||
}
|
||||
|
||||
const newTimeEntry: TimeEntryInterface = {
|
||||
projectId: selectedProject!.id,
|
||||
taskId: selectedTask!.id,
|
||||
date: new Date().toISOString(),
|
||||
notes: notes
|
||||
}
|
||||
|
||||
console.log(newTimeEntry)
|
||||
|
||||
const saveNewTimeEntryResponse = await saveNewTimeEntry(newTimeEntry)
|
||||
|
||||
console.log(saveNewTimeEntryResponse)
|
||||
})
|
||||
}
|
||||
|
||||
|
63
src/UseCases/SetupHarvest.ts
Normal file
63
src/UseCases/SetupHarvest.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import * as vscode from "vscode"
|
||||
import Harvest from "../Entities/Harvest"
|
||||
import ProjectInterface from "../Entities/Interfaces/ProjectInterface"
|
||||
import UserInterface from "../Entities/Interfaces/UserInterface"
|
||||
import Project from "../Entities/Project"
|
||||
import ProjectCollection from "../Entities/ProjectCollection"
|
||||
import User from "../Entities/User"
|
||||
import getProjectsAssignments from "./getProjectsAssignments"
|
||||
import getUser from "./getUser"
|
||||
|
||||
async function SetupHarvest (context: vscode.ExtensionContext): Promise<boolean> {
|
||||
const harvest = new Harvest()
|
||||
let projectCollection = new ProjectCollection()
|
||||
let user = new User()
|
||||
|
||||
const accountId: string = context.globalState.get('accountId') || ''
|
||||
const accessToken: string = context.globalState.get('accessToken') || ''
|
||||
|
||||
if (!accountId || !accessToken) {
|
||||
vscode.window.showErrorMessage('Run "Harvest: Login" Command before trying to puch time')
|
||||
return false
|
||||
}
|
||||
|
||||
harvest.accountId = accountId
|
||||
harvest.accessToken = accessToken
|
||||
|
||||
if (!user.id) {
|
||||
vscode.window.showInformationMessage('Authenticating with Harvest')
|
||||
let userProps: UserInterface
|
||||
try {
|
||||
userProps = await getUser()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
vscode.window.showErrorMessage('Could not retrieve user data from Harvest')
|
||||
return false
|
||||
}
|
||||
if (!userProps.id) {
|
||||
vscode.window.showErrorMessage('Could not retrieve user data from Harvest')
|
||||
return false
|
||||
}
|
||||
user.destructor()
|
||||
user = new User(userProps)
|
||||
}
|
||||
|
||||
if (projectCollection.elements.length <= 0) {
|
||||
vscode.window.showInformationMessage('Getting Projects from Harvest')
|
||||
try {
|
||||
let projectsData = await getProjectsAssignments()
|
||||
const projects = projectsData.map((p: ProjectInterface) => {
|
||||
return new Project(p)
|
||||
})
|
||||
projectCollection.addMany(projects)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
vscode.window.showErrorMessage('Could not retrieve uer projects')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export default SetupHarvest
|
@ -1,36 +1,50 @@
|
||||
import axios from 'axios'
|
||||
import Harvest from '../Entities/Harvest'
|
||||
import ErrorMessage from '../Constants/ErrorMessageInterface'
|
||||
import ErrorMessages from '../Constants/ErrorMessages'
|
||||
import ProjectInterface from '../Entities/Interfaces/ProjectInterface'
|
||||
|
||||
const getProjectsAssignments = async (): Promise<ProjectInterface[] | ErrorMessage> => {
|
||||
const getProjectsAssignments = async (): Promise<ProjectInterface[]> => {
|
||||
const harvest = new Harvest()
|
||||
let projectsResponse: any
|
||||
try {
|
||||
projectsResponse = await axios.get(
|
||||
'https://api.harvestapp.com/v2/users/me/project_assignments',
|
||||
{ headers : harvest.headers }
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return ErrorMessages[1]
|
||||
}
|
||||
|
||||
const projects = projectsResponse.data.project_assignments.map((p: any) => {
|
||||
return {
|
||||
id: p.project.id,
|
||||
name: p.project.name,
|
||||
tasks: p.task_assignments.map((t: any) => {
|
||||
return {
|
||||
id: t.task.id,
|
||||
name: t.task.name
|
||||
}
|
||||
})
|
||||
}
|
||||
const projectResponses: any = await _getManyProjectPagesFromApi(harvest)
|
||||
|
||||
let projects: ProjectInterface[] = []
|
||||
projectResponses.forEach((reponse: any) => {
|
||||
const projectResponss: ProjectInterface[] = reponse.data.project_assignments.map((p: any) => {
|
||||
return {
|
||||
id: p.project.id,
|
||||
name: p.project.name,
|
||||
tasks: p.task_assignments.map((t: any) => {
|
||||
return {
|
||||
id: t.task.id,
|
||||
name: t.task.name
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
projects = [...projects, ...projectResponss]
|
||||
})
|
||||
|
||||
return projects
|
||||
}
|
||||
|
||||
let projectPageRresonses: any = []
|
||||
|
||||
const _getManyProjectPagesFromApi = async (harvest: Harvest, nextPageUrl?: string): Promise<any> => {
|
||||
let projectsResponse: any
|
||||
try {
|
||||
projectsResponse = await axios.get(
|
||||
nextPageUrl || 'https://api.harvestapp.com/v2/users/me/project_assignments',
|
||||
{ headers : harvest.headers }
|
||||
)
|
||||
|
||||
projectPageRresonses.push(projectsResponse)
|
||||
|
||||
if (projectsResponse.data.links.next) await _getManyProjectPagesFromApi(harvest, projectsResponse.data.links.next)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
return projectPageRresonses
|
||||
}
|
||||
|
||||
export default getProjectsAssignments
|
38
src/UseCases/saveNewTimeEntry.ts
Normal file
38
src/UseCases/saveNewTimeEntry.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import axios from 'axios'
|
||||
import Harvest from '../Entities/Harvest'
|
||||
import TimeEntryInterface from '../Entities/Interfaces/TimeEntryInterface'
|
||||
|
||||
const saveNewTimeEntry = async (timeEntry: TimeEntryInterface): Promise<TimeEntryInterface> => {
|
||||
const harvest = new Harvest()
|
||||
|
||||
const body = {
|
||||
project_id: timeEntry.projectId,
|
||||
task_id: timeEntry.taskId,
|
||||
spent_date: timeEntry.date,
|
||||
notes: timeEntry.notes
|
||||
}
|
||||
|
||||
let timeEntryResponse: any
|
||||
try {
|
||||
timeEntryResponse = await axios.post(
|
||||
'https://api.harvestapp.com/v2/time_entries',
|
||||
body,
|
||||
{ headers: harvest.headers }
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
const createdTimeEntry: TimeEntryInterface = {
|
||||
id: timeEntryResponse.data.id,
|
||||
projectId: timeEntryResponse.data.project.id,
|
||||
date: timeEntryResponse.data.spent_date,
|
||||
taskId: timeEntryResponse.data.task.id,
|
||||
notes: timeEntryResponse.data.notes,
|
||||
isRunning: timeEntryResponse.data.is_running
|
||||
}
|
||||
|
||||
return createdTimeEntry
|
||||
}
|
||||
|
||||
export default saveNewTimeEntry
|
@ -2,11 +2,12 @@ import ErrorMessage from "../../Constants/ErrorMessageInterface"
|
||||
import Harvest from "../../Entities/Harvest"
|
||||
import ProjectInterface from "../../Entities/Interfaces/ProjectInterface"
|
||||
import Project from "../../Entities/Project"
|
||||
import ProjectCollection from "../../Entities/ProjectCollection"
|
||||
import getProjectsAssignments from '../../UseCases/getProjectsAssignments'
|
||||
import env from '../env'
|
||||
import UnitTest from "../UnitTestInterface"
|
||||
|
||||
const projectCreateInstance = async (): Promise<boolean> => {
|
||||
const projectCreateInstance = (): boolean => {
|
||||
const input: ProjectInterface = {
|
||||
id: 10203,
|
||||
name: 'Test Project',
|
||||
@ -53,9 +54,69 @@ const getProjectsAssignmentsFromApi = async (): Promise<boolean> => {
|
||||
else return false
|
||||
}
|
||||
|
||||
const projectCollectionAddOne = (): boolean => {
|
||||
new ProjectCollection().destructor()
|
||||
|
||||
const input: ProjectInterface = {
|
||||
id: 10203,
|
||||
name: 'Test Project',
|
||||
tasks: [{
|
||||
id: 123,
|
||||
name: 'Test Task'
|
||||
}]
|
||||
}
|
||||
|
||||
const expectedOutput: ProjectInterface = {
|
||||
id: 10203,
|
||||
name: 'Test Project',
|
||||
tasks: [{
|
||||
id: 123,
|
||||
name: 'Test Task'
|
||||
}]
|
||||
}
|
||||
|
||||
const project = new Project(input)
|
||||
const collection = new ProjectCollection()
|
||||
collection.addOne(project)
|
||||
|
||||
if (JSON.stringify(collection.elements[0].props) === JSON.stringify(expectedOutput)) return true
|
||||
else return false
|
||||
}
|
||||
|
||||
const projectCollectionAddMany = (): boolean => {
|
||||
new ProjectCollection().destructor()
|
||||
|
||||
const input: ProjectInterface = {
|
||||
id: 10203,
|
||||
name: 'Test Project',
|
||||
tasks: [{
|
||||
id: 123,
|
||||
name: 'Test Task'
|
||||
}]
|
||||
}
|
||||
|
||||
const expectedOutput: ProjectInterface = {
|
||||
id: 10203,
|
||||
name: 'Test Project',
|
||||
tasks: [{
|
||||
id: 123,
|
||||
name: 'Test Task'
|
||||
}]
|
||||
}
|
||||
|
||||
const project = new Project(input)
|
||||
const collection = new ProjectCollection()
|
||||
collection.addMany([project])
|
||||
|
||||
if (JSON.stringify(collection.elements[0].props) === JSON.stringify(expectedOutput)) return true
|
||||
else return false
|
||||
}
|
||||
|
||||
const unitTests: UnitTest[] = [
|
||||
{ name: 'Entity | Project Create Instance', test: projectCreateInstance },
|
||||
{ name: 'Use Case | Get Projects From Api', test: getProjectsAssignmentsFromApi },
|
||||
{ name: 'Collection | Add One To Project Collection', test: projectCollectionAddOne },
|
||||
{ name: 'Collection | Add Many To Project Collection', test: projectCollectionAddMany },
|
||||
]
|
||||
|
||||
export default unitTests
|
Loading…
x
Reference in New Issue
Block a user