// 2. Teil des Go-Tutorials // Listing-Auszüge aus dem Heft // Der komplette Beispielcode // des GitHub Progress Monitor ist unter // https://github.com/themue/ghpm/ // zu finden. Listing 1: RepoEventor type RepoEventor struct { owner string repo string } func NewRepoEventor(o, r string) *RepoEventor { return &RepoEventor{ owner: o, repo: r, } } Listing 2: Field Tags type Event struct { Type string `json:"type"` Public bool `json:"public"` Payload EventPayload `json:"payload"` Repo EventRepo `json:"repo"` Actor EventActor `json:"actor"` Org EventOrg `json:"org"` CreatedAt time.Time `json:"created_at"` ID string `json:"id"` } type Events []Event Listing 3: Abruf der Daten // Prepare request. api := "https://api.github.com/repos/%s/%s/events" url := fmt.Sprintf(api, e.owner, e.repo) req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err } Listing 4: Ausführung eines Request // Perform request. var client http.Client resp, err := client.Do(req) if err != nil { return nil, err } switch resp.StatusCode { case http.StatusOK: // Everything fine. case http.StatusNotModified: // Nothing to see here. return Events{}, nil default: return nil, fmt.Errorf("HTTP status code %d", resp.StatusCode) } buf, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } err = resp.Body.Close() if err != nil { return nil, err } Listing 5: Rücklieferung der Ergebnisse // Unmarshall events. var events Events err = json.Unmarshal(buf, &events) if err != nil { return nil, err } return events, nil Listing 6: Optimierung mit ETag // Use ETag if set. if e.eTag != "" { req.Header.Set("If-None-Match", e.eTag) } ... // Read ETag from response. e.eTag = resp.Header.Get("ETag") Listing 7: Interface für optionale Parameter // Options contains the superset of all options. type Options struct { Authentication Authentication ... } // Option applies on option to the passed options. type Option func(os *Options) type RepoEventor struct { ... options *Options } Listing 8: Basic Authentication type basicAuth struct { username string password string } func (a *basicAuth) Apply(req *http.Request) error { req.SetBasicAuth(a.username, a.password) return nil } func BasicAuth(username, password string) Option { return func(os *Options) { os.Authentication = &basicAuth{ username: username, password: password, } } } Listing 9: OAuth-2-Token type oauth2Token string func (t oauth2Token) Apply(req *http.Request) error { req.Header.Set("Authorization", "token "+string(t)) return nil } func OAuth2Token(token string) Option { return func(os *Options) { os.Authentication = oauth2Token(token) } } Listing 10: Kontruktor mit variadischem Parameter func NewRepoEventor(o, r string, os ...Option) *RepoEventor { e := &RepoEventor{ owner: o, repo: r, options: &Options{}, } // Apply options. for _, o := range os { o(e.options) } return e } Listing 11: Aggregator mit variadischem Parameter func Events( es github.Events, eas ...EventsAnalyzer) (Aggregate, error) { var a Aggregate = Aggregate{} var err error for _, analyze := range eas { a, err = analyze(es, a) if err != nil { return nil, err } } return a, nil } Listing 12: Event-Typen zählen func TypeCounter( es github.Events, a Aggregate) (Aggregate, error) { for _, e := range es { var c int t := "type/" + e.Type c, _ = a[t].(int) c++ a[t] = c } return a, nil } Listing 13: Fabrik-Funktion func CreateActorFilter(login string) EventsAnalyzer { return func( es github.Events, a Aggregate) (Aggregate, error) { for _, e := range es { if e.Actor.Login != login { continue } var c int al := "actor/" + e.Actor.Login c, _ = a[al].(int) c++ a[al] = c } return a, nil } }