-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
310 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"id": "job_5ca1ab1eca11ab1e", | ||
"app_id": "pi3243fa", | ||
"name": "api bulk job", | ||
"state": "running", | ||
"updated_at": 1438944983, | ||
"created_at": 1438944983, | ||
"completed_at": null, | ||
"links": { | ||
"error": "https://api.intercom.io/jobs/job_5ca1ab1eca11ab1e/error", | ||
"self": "https://api.intercom.io/jobs/job_5ca1ab1eca11ab1e" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package intercom | ||
|
||
import "fmt" | ||
|
||
// JobService builds jobs to process | ||
type JobService struct { | ||
Repository JobRepository | ||
} | ||
|
||
// The state of a Job | ||
type JobState int | ||
|
||
const ( | ||
PENDING JobState = iota | ||
RUNNING | ||
COMPLETED | ||
FAILED | ||
) | ||
|
||
var jobStates = [...]string{ | ||
"pending", | ||
"running", | ||
"completed", | ||
"failed", | ||
} | ||
|
||
// A JobRequest represents a new job to be sent to Intercom | ||
type JobRequest struct { | ||
JobData *JobData `json:"job,omitempty"` | ||
Items []*JobItem `json:"items,omitempty"` | ||
|
||
bulkType string | ||
} | ||
|
||
// A JobResponse represents a job enqueud on Intercom | ||
type JobResponse struct { | ||
ID string `json:"id,omitempty"` | ||
AppID string `json:"app_id,omitempty"` | ||
UpdatedAt int64 `json:"updated_at,omitempty"` | ||
CreatedAt int64 `json:"created_at,omitempty"` | ||
CompletedAt int64 `json:"completed_at,omitempty"` | ||
ClosingAt int64 `json:"closing_at,omitempty"` | ||
Name string `json:"name,omitempty"` | ||
State string `json:"job_state,omitempty"` | ||
Links map[string]string `json:"links,omitempty"` | ||
} | ||
|
||
// JobData is a payload that can be used to identify an existing Job to append to. | ||
type JobData struct { | ||
ID string `json:"id,omitempty"` | ||
} | ||
|
||
// NewUserJob creates a new Job for processing Users. | ||
func (js *JobService) NewUserJob(items ...*JobItem) (JobResponse, error) { | ||
job := JobRequest{Items: items, bulkType: "users"} | ||
return js.Repository.save(&job) | ||
} | ||
|
||
// NewEventJob creates a new Job for processing Events. | ||
func (js *JobService) NewEventJob(items ...*JobItem) (JobResponse, error) { | ||
job := JobRequest{Items: items, bulkType: "events"} | ||
return js.Repository.save(&job) | ||
} | ||
|
||
// Append User items to existing Job | ||
func (js *JobService) AppendUsers(id string, items ...*JobItem) (JobResponse, error) { | ||
job := JobRequest{JobData: &JobData{ID: id}, Items: items, bulkType: "users"} | ||
return js.Repository.save(&job) | ||
} | ||
|
||
// Append Event items to existing Job | ||
func (js *JobService) AppendEvents(id string, items ...*JobItem) (JobResponse, error) { | ||
job := JobRequest{JobData: &JobData{ID: id}, Items: items, bulkType: "events"} | ||
return js.Repository.save(&job) | ||
} | ||
|
||
// Find existing Job | ||
func (js *JobService) Find(id string) (JobResponse, error) { | ||
return js.Repository.find(id) | ||
} | ||
|
||
func (j JobResponse) String() string { | ||
return fmt.Sprintf("[intercom] job { id: %s, name: %s}", j.ID, j.Name) | ||
} | ||
|
||
func (state JobState) String() string { | ||
return jobStates[state] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package intercom | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/intercom/intercom-go/interfaces" | ||
) | ||
|
||
// JobRepository defines the interface for working with Jobs. | ||
type JobRepository interface { | ||
save(job *JobRequest) (JobResponse, error) | ||
find(id string) (JobResponse, error) | ||
} | ||
|
||
// JobAPI implements TagRepository | ||
type JobAPI struct { | ||
httpClient interfaces.HTTPClient | ||
} | ||
|
||
func (api JobAPI) save(job *JobRequest) (JobResponse, error) { | ||
for i := range job.Items { | ||
obj := job.Items[i].Data | ||
switch obj.(type) { | ||
case *User: | ||
user := obj.(*User) | ||
job.Items[i].Data = RequestUserMapper{}.ConvertUser(user) | ||
} | ||
} | ||
savedJob := JobResponse{} | ||
data, err := api.httpClient.Post(fmt.Sprintf("/bulk/%s", job.bulkType), job) | ||
if err != nil { | ||
return savedJob, err | ||
} | ||
err = json.Unmarshal(data, &savedJob) | ||
return savedJob, err | ||
} | ||
|
||
func (api JobAPI) find(id string) (JobResponse, error) { | ||
fetchedJob := JobResponse{} | ||
data, err := api.httpClient.Get(fmt.Sprintf("/jobs/%s", id), nil) | ||
if err != nil { | ||
return fetchedJob, err | ||
} | ||
err = json.Unmarshal(data, &fetchedJob) | ||
return fetchedJob, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package intercom | ||
|
||
import ( | ||
"io/ioutil" | ||
"testing" | ||
) | ||
|
||
func TestJobAPISaveUser(t *testing.T) { | ||
http := TestJobHTTPClient{t: t, expectedURI: "/bulk/users", fixtureFilename: "fixtures/job.json"} | ||
api := JobAPI{httpClient: &http} | ||
user := User{UserID: "1234"} | ||
job := JobRequest{Items: []*JobItem{NewUserJobItem(&user, JOB_POST)}, bulkType: "users"} | ||
http.f = func(job *JobRequest) { | ||
if job.Items[0].DataType != "user" { | ||
t.Errorf("job item was of wrong data type, expected %s, was %s", "user", job.Items[0].DataType) | ||
} | ||
if job.Items[0].Data.(requestUser).UserID != "1234" { | ||
t.Errorf("wrong user id sent") | ||
} | ||
} | ||
savedJob, _ := api.save(&job) | ||
if savedJob.ID != "job_5ca1ab1eca11ab1e" { | ||
t.Errorf("Did not respond with correct job") | ||
} | ||
} | ||
|
||
func TestJobAPISaveEvent(t *testing.T) { | ||
http := TestJobHTTPClient{t: t, expectedURI: "/bulk/events", fixtureFilename: "fixtures/job.json"} | ||
api := JobAPI{httpClient: &http} | ||
event := Event{UserID: "1234"} | ||
job := JobRequest{Items: []*JobItem{NewEventJobItem(&event)}, bulkType: "events"} | ||
http.f = func(job *JobRequest) { | ||
if job.Items[0].DataType != "event" { | ||
t.Errorf("job item was of wrong data type, expected %s, was %s", "event", job.Items[0].DataType) | ||
} | ||
if job.Items[0].Data.(*Event).UserID != "1234" { | ||
t.Errorf("wrong user id sent") | ||
} | ||
} | ||
savedJob, _ := api.save(&job) | ||
if savedJob.ID != "job_5ca1ab1eca11ab1e" { | ||
t.Errorf("Did not respond with correct job") | ||
} | ||
} | ||
|
||
type TestJobHTTPClient struct { | ||
TestHTTPClient | ||
t *testing.T | ||
f func(job *JobRequest) | ||
fixtureFilename string | ||
expectedURI string | ||
} | ||
|
||
func (t *TestJobHTTPClient) Post(uri string, body interface{}) ([]byte, error) { | ||
if t.expectedURI != uri { | ||
t.t.Errorf("Wrong endpoint called") | ||
} | ||
if t.f != nil { | ||
t.f(body.(*JobRequest)) | ||
} | ||
return ioutil.ReadFile(t.fixtureFilename) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package intercom | ||
|
||
// A JobItem is an item to be processed as part of a bulk Job | ||
type JobItem struct { | ||
Method string `json:"method"` | ||
DataType string `json:"data_type"` | ||
Data interface{} `json:"data"` | ||
} | ||
|
||
// NewUserJobItem creates a JobItem that holds an User. | ||
// It can take either a JOB_POST (for updates) or JOB_DELETE (for deletes) method. | ||
func NewUserJobItem(user *User, method JobItemMethod) *JobItem { | ||
return &JobItem{Method: method.String(), DataType: "user", Data: user} | ||
} | ||
|
||
// NewEventJobItem creates a JobItem that holds an Event. | ||
func NewEventJobItem(event *Event) *JobItem { | ||
return &JobItem{Method: JOB_POST.String(), DataType: "event", Data: event} | ||
} | ||
|
||
type JobItemMethod int | ||
|
||
const ( | ||
JOB_POST JobItemMethod = iota | ||
JOB_DELETE | ||
) | ||
|
||
var jobItemMethods = [...]string{ | ||
"post", | ||
"delete", | ||
} | ||
|
||
func (state JobItemMethod) String() string { | ||
return jobItemMethods[state] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package intercom | ||
|
||
import "testing" | ||
|
||
func TestNewJob(t *testing.T) { | ||
repo := &TestJobRepository{t: t} | ||
repo.f = func(job *JobRequest) { | ||
if job.Items[0].Method != JOB_POST.String() { | ||
repo.t.Errorf("Wrong job method") | ||
} | ||
u := job.Items[0].Data.(*User) | ||
if u.Email != "foo@bar.com" { | ||
repo.t.Errorf("Wrong user email") | ||
} | ||
} | ||
user := User{Email: "foo@bar.com"} | ||
js := JobService{Repository: repo} | ||
js.NewUserJob(NewUserJobItem(&user, JOB_POST)) | ||
} | ||
|
||
func TestAppendJob(t *testing.T) { | ||
repo := &TestJobRepository{t: t} | ||
js := JobService{Repository: repo} | ||
newJob, _ := js.NewUserJob() | ||
|
||
repo.f = func(job *JobRequest) { | ||
if job.Items[0].Method != JOB_POST.String() { | ||
repo.t.Errorf("Wrong job method") | ||
} | ||
u := job.Items[0].Data.(*User) | ||
if u.Email != "foo@bar.com" { | ||
repo.t.Errorf("Wrong user email") | ||
} | ||
} | ||
user := User{Email: "foo@bar.com"} | ||
|
||
js.AppendUsers(newJob.ID, NewUserJobItem(&user, JOB_POST)) | ||
} | ||
|
||
type TestJobRepository struct { | ||
t *testing.T | ||
f func(job *JobRequest) | ||
} | ||
|
||
func (api *TestJobRepository) save(job *JobRequest) (JobResponse, error) { | ||
if api.f != nil { | ||
api.f(job) | ||
} | ||
return JobResponse{}, nil | ||
} | ||
|
||
func (api *TestJobRepository) find(id string) (JobResponse, error) { | ||
return JobResponse{}, nil | ||
} |