Skip to content

Commit

Permalink
created the batch job object (#112)
Browse files Browse the repository at this point in the history
* created the batch job object

* added the client

* added unit tests

* renamed file

* added batch job functions

* added batch job functions

* added the unit tests for the job methods

* added examples

* updated the readme

* updated comments
  • Loading branch information
g8rswimmer authored Mar 1, 2022
1 parent 1eeea95 commit 3d5666b
Show file tree
Hide file tree
Showing 9 changed files with 1,045 additions and 0 deletions.
2 changes: 2 additions & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ Here are the current twitter `v2` API features supported:
* [spaces tweets](./_examples/spaces-tweets)
* [Spaces Search](https://developer.twitter.com/en/docs/twitter-api/spaces/search/introduction)
* [space search](./_examples/spaces-search)
* [Compliance Batch](https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/introduction)
* [compliance batch](./_examples/compliance-batch)

## Rate Limiting
With each response, the rate limits from the response header is returned. This allows the caller to manage any limits that are imposed. Along with the response, errors that are returned may have rate limits as well. If the error occurs after the request is sent, then rate limits may apply and are returned.
Expand Down
7 changes: 7 additions & 0 deletions v2/_examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,10 @@ This [example](./spaces-search) demonstrates space search API.
```
go run *.go -token=YOUR_API_TOKEN -query=YOUR_QUERY
```

## Compliance Batch
This [example](./compliance-batch) demonstrates how to send a compliance batch.

```
go run *.go -token=YOUR_API_TOKEN -type=[tweets,users] -upload=FILE_OF_IDS
```
104 changes: 104 additions & 0 deletions v2/_examples/compliance-batch/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package main

import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"time"

twitter "github.com/g8rswimmer/go-twitter/v2"
)

type authorize struct {
Token string
}

func (a authorize) Add(req *http.Request) {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Token))
}

/**
In order to run, the user will need to provide the bearer token and the list of tweet ids.
**/
func main() {
token := flag.String("token", "", "twitter API token")
jobType := flag.String("type", "", "job type")
upload := flag.String("upload", "", "upload file")
flag.Parse()

client := &twitter.Client{
Authorizer: authorize{
Token: *token,
},
Client: http.DefaultClient,
Host: "https://api.twitter.com",
}

fmt.Println("Compliance Batch Job Example")

opts := twitter.CreateComplianceBatchJobOpts{
Name: "go twitter job example",
}

// 1. Create a compliance batch job
fmt.Println("1. Create a compliance batch job")
complianceResponse, err := client.CreateComplianceBatchJob(context.Background(), twitter.ComplianceBatchJobType(*jobType), opts)
if err != nil {
log.Panicf("create compliance job error: %v", err)
}

enc, err := json.MarshalIndent(complianceResponse, "", " ")
if err != nil {
log.Panicf("create compliance job error: %v", err)
}
fmt.Println(string(enc))

job := complianceResponse.Raw.Job

// 2. Upload ids from file
fmt.Println("2. Upload ids from file")
f, err := os.Open(*upload)
if err != nil {
log.Panicf("open upload file error: %v", err)
}
defer f.Close()

err = job.Upload(context.Background(), f)
if err != nil {
log.Panicf("upload ids error: %v", err)
}

// 3. Check the job status
fmt.Println("3. Check the job status")
for {
time.Sleep(time.Second)

resp, err := client.ComplianceBatchJob(context.Background(), job.ID)
if err != nil {
log.Panicf("check status error: %v", err)
}
jobStatus := resp.Raw.Job
fmt.Println("Status: " + jobStatus.Status)
if jobStatus.Status != twitter.ComplianceBatchJobStatusInProgress {
break
}
}

// 4. Download results
fmt.Println("4. Download results")
downloadResponse, err := job.Download(context.Background())
if err != nil {
log.Panicf("download results error: %v", err)
}

enc, err = json.MarshalIndent(downloadResponse, "", " ")
if err != nil {
log.Panicf("download results error: %v", err)
}
fmt.Println(string(enc))

}
194 changes: 194 additions & 0 deletions v2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3592,3 +3592,197 @@ func (c *Client) SpacesSearch(ctx context.Context, query string, opts SpacesSear
RateLimit: rl,
}, nil
}

// CreateComplianceBatchJob creates a new compliance job for tweet or user IDs.
func (c *Client) CreateComplianceBatchJob(ctx context.Context, jobType ComplianceBatchJobType, opts CreateComplianceBatchJobOpts) (*CreateComplianceBatchJobResponse, error) {
switch {
case len(jobType) == 0:
return nil, fmt.Errorf("create compliance batch job: a type is required: %w", ErrParameter)
default:
}

request := struct {
Type ComplianceBatchJobType `json:"type"`
Name string `json:"name,omitempty"`
Resumable bool `json:"resumable,omitempty"`
}{
Type: jobType,
Name: opts.Name,
Resumable: opts.Resumable,
}

enc, err := json.Marshal(request)
if err != nil {
return nil, fmt.Errorf("create compliance batch job request encode: %w", err)
}

req, err := http.NewRequestWithContext(ctx, http.MethodPost, complainceJobsEndpont.url(c.Host), bytes.NewReader(enc))
if err != nil {
return nil, fmt.Errorf("create compliance batch job request: %w", err)
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")

resp, err := c.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("create compliance batch job response: %w", err)
}
defer resp.Body.Close()

decoder := json.NewDecoder(resp.Body)

rl := rateFromHeader(resp.Header)

if resp.StatusCode != http.StatusOK {
e := &ErrorResponse{}
if err := decoder.Decode(e); err != nil {
return nil, &HTTPError{
Status: resp.Status,
StatusCode: resp.StatusCode,
URL: resp.Request.URL.String(),
RateLimit: rl,
}
}
e.StatusCode = resp.StatusCode
e.RateLimit = rl
return nil, e
}

raw := &ComplianceBatchJobRaw{}

if err := decoder.Decode(&raw); err != nil {
return nil, &ResponseDecodeError{
Name: "create compliance batch job",
Err: err,
RateLimit: rl,
}
}
raw.Job.client = c.Client

return &CreateComplianceBatchJobResponse{
Raw: raw,
RateLimit: rl,
}, nil
}

// ComplianceBatchJob returns a single compliance job
func (c *Client) ComplianceBatchJob(ctx context.Context, id string) (*ComplianceBatchJobResponse, error) {
switch {
case len(id) == 0:
return nil, fmt.Errorf("compliance batch job: a type is required: %w", ErrParameter)
default:
}

ep := complainceJobsEndpont.url(c.Host) + "/" + id
req, err := http.NewRequestWithContext(ctx, http.MethodGet, ep, nil)
if err != nil {
return nil, fmt.Errorf("compliance batch job request: %w", err)
}
req.Header.Add("Accept", "application/json")

resp, err := c.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("compliance batch job response: %w", err)
}
defer resp.Body.Close()

decoder := json.NewDecoder(resp.Body)

rl := rateFromHeader(resp.Header)

if resp.StatusCode != http.StatusOK {
e := &ErrorResponse{}
if err := decoder.Decode(e); err != nil {
return nil, &HTTPError{
Status: resp.Status,
StatusCode: resp.StatusCode,
URL: resp.Request.URL.String(),
RateLimit: rl,
}
}
e.StatusCode = resp.StatusCode
e.RateLimit = rl
return nil, e
}

raw := &ComplianceBatchJobRaw{}

if err := decoder.Decode(&raw); err != nil {
return nil, &ResponseDecodeError{
Name: "compliance batch job",
Err: err,
RateLimit: rl,
}
}
raw.Job.client = c.Client

return &ComplianceBatchJobResponse{
Raw: raw,
RateLimit: rl,
}, nil
}

// ComplianceBatchJobLookup returns a list of compliance jobs
func (c *Client) ComplianceBatchJobLookup(ctx context.Context, jobType ComplianceBatchJobType, opts ComplianceBatchJobLookupOpts) (*ComplianceBatchJobLookupResponse, error) {
switch {
case len(jobType) == 0:
return nil, fmt.Errorf("compliance batch job lookup: a type is required: %w", ErrParameter)
default:
}

ep := complainceJobsEndpont.url(c.Host)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, ep, nil)
if err != nil {
return nil, fmt.Errorf("compliance batch job lookup request: %w", err)
}
req.Header.Add("Accept", "application/json")

opts.addQuery(req)
q := req.URL.Query()
q.Add("type", string(jobType))
req.URL.RawQuery = q.Encode()

resp, err := c.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("compliance batch job lookup response: %w", err)
}
defer resp.Body.Close()

decoder := json.NewDecoder(resp.Body)

rl := rateFromHeader(resp.Header)

if resp.StatusCode != http.StatusOK {
e := &ErrorResponse{}
if err := decoder.Decode(e); err != nil {
return nil, &HTTPError{
Status: resp.Status,
StatusCode: resp.StatusCode,
URL: resp.Request.URL.String(),
RateLimit: rl,
}
}
e.StatusCode = resp.StatusCode
e.RateLimit = rl
return nil, e
}

raw := &ComplianceBatchJobsRaw{}

if err := decoder.Decode(&raw); err != nil {
return nil, &ResponseDecodeError{
Name: "compliance batch job lookup",
Err: err,
RateLimit: rl,
}
}

for i := range raw.Jobs {
raw.Jobs[i].client = c.Client
}

return &ComplianceBatchJobLookupResponse{
Raw: raw,
RateLimit: rl,
}, nil
}
Loading

0 comments on commit 3d5666b

Please sign in to comment.