package insight import ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "os" "strings" ) const ( BaseApiUrl = "https://jds.projectinsight.net/api/" insightTokenFile = "insight.token" projectEndpoint = "" projectListEndpoint = "project/list?ids=" timeEntryForUserInRangeEndpoint = "time-entry/user-date-range?startDate=2020-01-01&endDate=%s&userId=%s" userEndpoint = "user/list-active?modelProperties=Id,FirstName,LastName,EmailAddress" ) type ( Interconnect struct { Client *IClient DBConnector *DBConnector } IClient struct { client *http.Client tokens *map[string]string } Project struct { BillableExpenseVarianceEarnedValueBillableVsWorkBillableTotalPercent float64 `json:"BillableExpense_VarianceEarnedValueBillableVsWorkBillableTotalPercent,omitempty"` CompanyDefaultId string `json:"CompanyDefault_Id,omitempty"` CustomFieldValueId string `json:"CustomFieldValue_Id,omitempty"` DepartmentId string `json:"Department_Id,omitempty"` DurationSeconds int64 DurationString string EndDateTimeUTC string EstimateAtCompletionToTargetTotalVariance float64 `json:"EstimateAtCompletionToTargetTotal_Variance,omitempty"` EstimateAtCompletionToTargetTotalPercentVariance float64 `json:"EstimateAtCompletionToTargetTotalPercent_Variance,omitempty"` IsActive bool IsOpen bool LocationDefaultId string `json:"LocationDefault_Id,omitempty"` Name string ProfitSalesOrderActual float64 ProjectPriorityId string `json:"ProjectPriority_Id,omitempty"` ProjectState int64 `json:"project_state,omitempty"` ProjectStatusId string `json:"ProjectStatus_Id,omitempty"` ProjectTypeId string `json:"ProjectType_Id,omitempty"` ScheduleStartDate string StartDateTimeUTC string UrlFull string VarSalesOrderExpenseVsBillableExpense float64 VarSalesOrderExpenseVsBillableExpensePercent float64 VarSalesOrderExpenseVsInvoiceExpense float64 VarSalesOrderExpenseVsInvoiceExpensePercent float64 VarSalesOrderHoursVsBillableHours float64 VarSalesOrderHoursVsBillableHoursPercent float64 VarSalesOrderHoursVsInvoiceHours float64 VarSalesOrderHoursVsInvoiceHoursPercent float64 VarSalesOrderRateVsBillableRate float64 VarSalesOrderRateVsBillableRatePercent float64 VarSalesOrderRateVsInvoiceRate float64 VarSalesOrderRateVsInvoiceRatePercent float64 VarSalesOrderTimeVsBillableTime float64 VarSalesOrderTimeVsBillableTimePercent float64 VarSalesOrderTimeVsInvoiceTime float64 VarSalesOrderTimeVsInvoiceTimePercent float64 VarSalesOrderTotalVsBillableTotal float64 VarSalesOrderTotalVsBillableTotalPercent float64 VarSalesOrderTotalVsInvoiceTotal float64 VarSalesOrderTotalVsInvoiceTotalPercent float64 WorkPercentComplete float64 WorkSeconds int64 } TimeEntry struct { ActualHours float64 ActualHoursFormattedString string ActualTimeString string ActualTotal float64 BillableHours float64 BillableHoursFormattedString string BillableTimeString string BillableTotal float64 TimeEntryDate string `json:"Date,omitempty"` TimeEntryDescription string `json:"Description,omitempty"` ProjectId string `json:"Project_Id,omitempty"` RateBill float64 RateBurden float64 TaskId string `json:"Task_Id,omitempty"` TimeSheetId string `json:"TimeSheet_Id,omitempty"` UserId string `json:"User_Id,omitempty"` } User struct { Id string FirstName string LastName string EmailAddress string } ) // /*====================================================================================== interconnect ======================================================================================*/ func NewInsightConnect() *Interconnect { connect := Interconnect{} connect.Client = NewIClient() connect.DBConnector = &DBConnector{} return &connect } func (ic *Interconnect) ResetTables() { ic.DBConnector.CreateTables() } func (ic *Interconnect) UpdateUsers() { users := ic.Client.GetUsers() ic.DBConnector.UpdateUsers(users) } func (ic *Interconnect) UpdateTimeEntries() { users := ic.DBConnector.FetchEngineerUsers() for _, userPtr := range *users { user := *userPtr entries := ic.Client.GetTimeAllTimeEntriesForUserThroughDate(user.Id, "2022-04-28") ic.DBConnector.UpdateTimeEntries(entries) } } // // /*====================================================================================== IClient ======================================================================================*/ func NewIClient() *IClient { c := IClient{ client: &http.Client{}, tokens: cacheTokens(), } return &c } func (iClient *IClient) GetTimeAllTimeEntriesForUserThroughDate(userId string, endDate string) *[]*TimeEntry { urlString := fmt.Sprintf(timeEntryForUserInRangeEndpoint, endDate, userId) log.Println(urlString) req := iClient.createRequest(urlString, insightTokenFile) rawBytes := iClient.doGet(req) container := make([]*TimeEntry, 0, 1000) //do we want to make the default result size parametric? err := json.Unmarshal(*rawBytes, &container) if err != nil { log.Fatal(err) } return &container } func (iClient *IClient) GetProjectsInList(projectIds []string) *[]Project { ids := strings.Join(projectIds, ",") req := iClient.createRequest(projectListEndpoint+ids, insightTokenFile) rawBytes := iClient.doGet(req) container := make([]Project, 0, 1000) //do we want to make the default result size parametric? err := json.Unmarshal(*rawBytes, &container) if err != nil { log.Fatal(err) } return &container } func (iClient *IClient) GetUsers() *[]User { users := iClient.getRawUsers() for idx, user := range *users { user.EmailAddress = strings.ToLower(user.EmailAddress) user.EmailAddress = strings.ReplaceAll(user.EmailAddress, "jdsdesignonline.com", "jdsconsulting.net") user.EmailAddress = strings.ReplaceAll(user.EmailAddress, "jdsfaulkner.com", "jdsconsulting.net") (*users)[idx] = user } return users } func (iClient *IClient) getRawUsers() *[]User { req := iClient.createRequest(userEndpoint, insightTokenFile) rawBytes := iClient.doGet(req) container := make([]User, 0, 150) //do we want to make the default result size parametric? err := json.Unmarshal(*rawBytes, &container) if err != nil { log.Fatal(err) } return &container } func (iClient *IClient) doGet(req *http.Request) *[]byte { resp, err := iClient.client.Do(req) if err != nil { log.Fatal(err) } bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } return &bodyBytes } func (iClient *IClient) createRequest(urlEndpoint string, tokenFile string) *http.Request { reqUrl := BaseApiUrl + urlEndpoint token := (*iClient.tokens)[tokenFile] request, err := http.NewRequest("GET", reqUrl, nil) if err != nil { log.Fatal(err) } request.Header.Add("api-token", token) return request } // // /*====================================================================================== utility functions ======================================================================================*/ func cacheTokens() *map[string]string { files, err := os.ReadDir("./tokens/") ret := make(map[string]string) if err != nil { panic(err) } for _, file := range files { subPath := fmt.Sprintf("./tokens/%s", file.Name()) fileContents, err := os.ReadFile(subPath) if err != nil { log.Fatal(err) } ret[file.Name()] = string(fileContents) } return &ret } //