👩💻 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
|
@ -38,3 +38,5 @@ class ProjectCollection {
|
|||||||
return project
|
return project
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default ProjectCollection
|
||||||
|
@ -1,44 +1,84 @@
|
|||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
import Harvest from "../../Entities/Harvest"
|
import TaskInterface from "../../Entities/Interfaces/TaskInterface";
|
||||||
import UserInterface from "../../Entities/Interfaces/UserInterface";
|
import TimeEntryInterface from "../../Entities/Interfaces/TimeEntryInterface";
|
||||||
import User from "../../Entities/User"
|
import Project from "../../Entities/Project";
|
||||||
import getUser from "../getUser";
|
import ProjectCollection from "../../Entities/ProjectCollection";
|
||||||
|
import saveNewTimeEntry from "../saveNewTimeEntry";
|
||||||
|
import SetupHarvest from "../SetupHarvest"
|
||||||
|
|
||||||
|
|
||||||
function PunchTime (context: vscode.ExtensionContext): vscode.Disposable {
|
function PunchTime (context: vscode.ExtensionContext): vscode.Disposable {
|
||||||
return vscode.commands.registerCommand('harvest-vscode.punchTime', async () => {
|
return vscode.commands.registerCommand('harvest-vscode.punchTime', async () => {
|
||||||
const harvest = new Harvest()
|
let projectCollection = new ProjectCollection()
|
||||||
let user = new User()
|
const isHarvestSetup = await SetupHarvest(context)
|
||||||
|
if (!isHarvestSetup) return
|
||||||
|
|
||||||
const accountId: string = context.globalState.get('accountId') || ''
|
const projectNames = projectCollection.elements.map((p: Project) => {
|
||||||
const accessToken: string = context.globalState.get('accessToken') || ''
|
return p.name
|
||||||
|
})
|
||||||
|
|
||||||
if (!accountId || !accessToken) {
|
const selectedProjectName = await vscode.window.showQuickPick(projectNames, {
|
||||||
vscode.window.showErrorMessage('Run "Harvest: Login" Command before trying to puch time')
|
ignoreFocusOut: true,
|
||||||
|
placeHolder: 'Choost a Project'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!selectedProjectName) {
|
||||||
|
vscode.window.showWarningMessage('Must select a project to push time')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
harvest.accountId = accountId
|
const selectedProject = projectCollection.findByName(selectedProjectName)
|
||||||
harvest.accessToken = accessToken
|
|
||||||
|
|
||||||
if (!user.id) {
|
const taskNames = selectedProject?.tasks.map((t: TaskInterface) => {
|
||||||
let userProps: UserInterface
|
return t.name
|
||||||
try {
|
})
|
||||||
userProps = await getUser()
|
|
||||||
} catch (err) {
|
if (!taskNames) {
|
||||||
console.log(err)
|
vscode.window.showWarningMessage('No tasks defined for this project.')
|
||||||
vscode.window.showErrorMessage('Could not retrieve user data from Harvest')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!userProps.id) {
|
|
||||||
vscode.window.showErrorMessage('Could not retrieve user data from Harvest')
|
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
|
return
|
||||||
}
|
}
|
||||||
user.destructor()
|
|
||||||
user = new User(userProps)
|
const selectedTask = selectedProject?.tasks.find((t: TaskInterface) => {
|
||||||
vscode.window.showInformationMessage('Successfully authenticated with Harvest')
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(user)
|
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,23 +1,15 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import Harvest from '../Entities/Harvest'
|
import Harvest from '../Entities/Harvest'
|
||||||
import ErrorMessage from '../Constants/ErrorMessageInterface'
|
|
||||||
import ErrorMessages from '../Constants/ErrorMessages'
|
|
||||||
import ProjectInterface from '../Entities/Interfaces/ProjectInterface'
|
import ProjectInterface from '../Entities/Interfaces/ProjectInterface'
|
||||||
|
|
||||||
const getProjectsAssignments = async (): Promise<ProjectInterface[] | ErrorMessage> => {
|
const getProjectsAssignments = async (): Promise<ProjectInterface[]> => {
|
||||||
const harvest = new Harvest()
|
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) => {
|
const projectResponses: any = await _getManyProjectPagesFromApi(harvest)
|
||||||
|
|
||||||
|
let projects: ProjectInterface[] = []
|
||||||
|
projectResponses.forEach((reponse: any) => {
|
||||||
|
const projectResponss: ProjectInterface[] = reponse.data.project_assignments.map((p: any) => {
|
||||||
return {
|
return {
|
||||||
id: p.project.id,
|
id: p.project.id,
|
||||||
name: p.project.name,
|
name: p.project.name,
|
||||||
@ -29,8 +21,30 @@ const getProjectsAssignments = async (): Promise<ProjectInterface[] | ErrorMessa
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
projects = [...projects, ...projectResponss]
|
||||||
|
})
|
||||||
|
|
||||||
return projects
|
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
|
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 Harvest from "../../Entities/Harvest"
|
||||||
import ProjectInterface from "../../Entities/Interfaces/ProjectInterface"
|
import ProjectInterface from "../../Entities/Interfaces/ProjectInterface"
|
||||||
import Project from "../../Entities/Project"
|
import Project from "../../Entities/Project"
|
||||||
|
import ProjectCollection from "../../Entities/ProjectCollection"
|
||||||
import getProjectsAssignments from '../../UseCases/getProjectsAssignments'
|
import getProjectsAssignments from '../../UseCases/getProjectsAssignments'
|
||||||
import env from '../env'
|
import env from '../env'
|
||||||
import UnitTest from "../UnitTestInterface"
|
import UnitTest from "../UnitTestInterface"
|
||||||
|
|
||||||
const projectCreateInstance = async (): Promise<boolean> => {
|
const projectCreateInstance = (): boolean => {
|
||||||
const input: ProjectInterface = {
|
const input: ProjectInterface = {
|
||||||
id: 10203,
|
id: 10203,
|
||||||
name: 'Test Project',
|
name: 'Test Project',
|
||||||
@ -53,9 +54,69 @@ const getProjectsAssignmentsFromApi = async (): Promise<boolean> => {
|
|||||||
else return false
|
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[] = [
|
const unitTests: UnitTest[] = [
|
||||||
{ name: 'Entity | Project Create Instance', test: projectCreateInstance },
|
{ name: 'Entity | Project Create Instance', test: projectCreateInstance },
|
||||||
{ name: 'Use Case | Get Projects From Api', test: getProjectsAssignmentsFromApi },
|
{ 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
|
export default unitTests
|
Loading…
x
Reference in New Issue
Block a user