diff --git a/go.mod b/go.mod index 344627e..d4c3fd2 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/wahyd4/zendesk go 1.13 + +require github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b5e2922 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/main.go b/main.go index b952421..4d69bce 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,19 @@ package main -import "fmt" +import ( + "fmt" + + "github.com/wahyd4/zendesk/search" +) func main() { - fmt.Println("I am placeholder of application") + + app := search.InitAPP("data/organizations.json", "data/users.json", "data/tickets.json") + if err := app.Parse(); err != nil { + panic("cannot load data: " + err.Error()) + } + if err := app.BuildOrganisationIndex(); err != nil { + panic("failed to build indexes: " + err.Error()) + } + fmt.Println("---------------") } diff --git a/search/app.go b/search/app.go index c0e40a6..69aa4d7 100644 --- a/search/app.go +++ b/search/app.go @@ -1,15 +1,48 @@ package search import ( + "io/ioutil" + "github.com/wahyd4/zendesk/model" ) +const ( + OrganisationsKey = "organisations" + UsersKey = "users" + TicketsKey = "tickets" +) + // APP represents the application type APP struct { jsonContents map[string][]byte organisations map[string]*model.Organisation users map[string]*model.User tickets map[string]*model.Ticket + + organisationIndex map[string]map[string][]*model.Organisation + // userIndex map[string]map[string][]*model.User + // ticketIndex map[string]map[string][]*model.Ticket +} + +func InitAPP(organisationsFile, usersFile, ticketsFile string) *APP { + files := map[string]string{ + OrganisationsKey: organisationsFile, + UsersKey: usersFile, + TicketsKey: ticketsFile, + } + jsonContents := make(map[string][]byte) + for fileType, file := range files { + bytes, err := ioutil.ReadFile(file) + if err != nil { + panic("cannot init application due to unable to load:" + file) + } + jsonContents[fileType] = bytes + } + + return &APP{ + jsonContents: jsonContents, + organisationIndex: make(map[string]map[string][]*model.Organisation), + } } // FindOrganisation find a organisation by organisation ID diff --git a/search/index.go b/search/index.go new file mode 100644 index 0000000..919f8c4 --- /dev/null +++ b/search/index.go @@ -0,0 +1,82 @@ +package search + +import ( + "encoding/json" + "fmt" + + "github.com/wahyd4/zendesk/model" +) + +const ( + idKey = "_id" +) + +var ( + arrayKeys = []string{"domain_names", "tags"} + booleanKeys = []string{"shared_tickets"} + integerKeys = []string{"_id"} +) + +func (app *APP) BuildOrganisationIndex() error { + bytes := app.jsonContents[OrganisationsKey] + + var organisations []map[string]interface{} + + err := json.Unmarshal(bytes, &organisations) + if err != nil { + return fmt.Errorf("failed to unmarshal json to organisations array: %w", err) + } + + if len(organisations) == 0 { + return nil + } + searchIndex := app.organisationIndex + organisationsTemplate := organisations[0] + + for fieldKey := range organisationsTemplate { + + for _, organisation := range organisations { + organisationID := stringifyID(int(organisation[idKey].(float64))) + + if searchIndex[fieldKey] == nil { + searchIndex[fieldKey] = make(map[string][]*model.Organisation) + } + + fieldIndex := searchIndex[fieldKey] + + fieldValues := make([]string, 0) + + // process value based on different types + if OneOfTheKeys(integerKeys, fieldKey) { + fieldValues = append(fieldValues, stringifyID(int(organisation[fieldKey].(float64)))) + } else if OneOfTheKeys(booleanKeys, fieldKey) { + fieldValues = append(fieldValues, fmt.Sprintf("%t", organisation[fieldKey].(bool))) + } else if OneOfTheKeys(arrayKeys, fieldKey) { + fieldValues = toStringSlice(organisation[fieldKey].([]interface{})) + } else { + fieldValues = append(fieldValues, organisation[fieldKey].(string)) + } + + // build fieldValue based indeX + for _, fieldValue := range fieldValues { + if fieldIndex[fieldValue] == nil { + sameValueList := make([]*model.Organisation, 0) + fieldIndex[fieldValue] = sameValueList + } + + fieldIndex[fieldValue] = append(fieldIndex[fieldValue], app.FindOrganisation(organisationID)) + } + + } + } + return nil +} + +func OneOfTheKeys(keys []string, target string) bool { + for _, key := range keys { + if key == target { + return true + } + } + return false +} diff --git a/search/parse.go b/search/parse.go index 772d4a2..b8e537e 100644 --- a/search/parse.go +++ b/search/parse.go @@ -13,9 +13,9 @@ type dataHandler func(jsonContent []byte) error // Parse parse all json contents and build all data and it's relationships into app func (app *APP) Parse() error { dataHandlers := map[string]dataHandler{ - "organisations": app.LoadOrganisationsFromJSON, - "users": app.LoadUsersFromJSON, - "tickets": app.LoadTicketsFromJSON, + OrganisationsKey: app.LoadOrganisationsFromJSON, + UsersKey: app.LoadUsersFromJSON, + TicketsKey: app.LoadTicketsFromJSON, } for dataType, dataHandler := range dataHandlers { @@ -63,7 +63,7 @@ func (app *APP) LoadTicketsFromJSON(jsonContent []byte) error { func convertOrganisationViews(views []*view.Organisation) map[string]*model.Organisation { models := make(map[string]*model.Organisation) for _, view := range views { - models[stringID(view.ID)] = convertOrganisationView(view) + models[stringifyID(view.ID)] = convertOrganisationView(view) } return models } @@ -71,7 +71,7 @@ func convertOrganisationViews(views []*view.Organisation) map[string]*model.Orga func (app *APP) convertUserViews(views []*view.User) map[string]*model.User { models := make(map[string]*model.User) for _, view := range views { - models[stringID(view.ID)] = app.convertUserView(view) + models[stringifyID(view.ID)] = app.convertUserView(view) } return models } @@ -115,7 +115,7 @@ func (app *APP) convertUserView(view *view.User) *model.User { Email: view.Email, Phone: view.Phone, Signature: view.Signature, - Organization: app.FindOrganisation(stringID(view.OrganizationID)), + Organization: app.FindOrganisation(stringifyID(view.OrganizationID)), Tags: view.Tags, Suspended: view.Suspended, Role: view.Role, @@ -133,9 +133,9 @@ func (app *APP) convertTicketView(view *view.Ticket) *model.Ticket { Description: view.Description, Priority: view.Priority, Status: view.Status, - Submitter: app.FindUser(stringID(view.SubmitterID)), - Assignee: app.FindUser(stringID(view.AssigneeID)), - Organization: app.FindOrganisation(stringID(view.OrganizationID)), + Submitter: app.FindUser(stringifyID(view.SubmitterID)), + Assignee: app.FindUser(stringifyID(view.AssigneeID)), + Organization: app.FindOrganisation(stringifyID(view.OrganizationID)), Tags: view.Tags, HasIncidents: view.HasIncidents, DueAt: view.DueAt, diff --git a/search/util.go b/search/util.go index 382f573..338c0da 100644 --- a/search/util.go +++ b/search/util.go @@ -4,6 +4,14 @@ import ( "fmt" ) -func stringID(id int) string { +func stringifyID(id int) string { return fmt.Sprintf("%d", id) } + +func toStringSlice(arr []interface{}) []string { + result := make([]string, len(arr)) + for _, item := range arr { + result = append(result, item.(string)) + } + return result +}