func (ia *importerAcct) start() { ia.mu.Lock() defer ia.mu.Unlock() if ia.current != nil { return } rc := &RunContext{ // TODO: context plumbing Context: context.New(context.WithHTTPClient(ia.im.host.HTTPClient())), Host: ia.im.host, ia: ia, } ia.current = rc ia.stopped = false ia.lastRunStart = time.Now() go func() { log.Printf("Starting %v: %s", ia, ia.AccountLinkSummary()) err := ia.im.impl.Run(rc) if err != nil { log.Printf("%v error: %v", ia, err) } else { log.Printf("%v finished.", ia) } ia.mu.Lock() defer ia.mu.Unlock() ia.current = nil ia.stopped = false ia.lastRunDone = time.Now() ia.lastRunErr = err go ia.maybeStart() }() }
func (im extendedOAuth2) ServeCallback(w http.ResponseWriter, r *http.Request, ctx *importer.SetupContext) { if im.getUserInfo == nil { panic("No getUserInfo is provided, don't use the default ServeCallback!") } oauthConfig, err := im.auth(ctx) if err != nil { httputil.ServeError(w, r, fmt.Errorf("Error getting oauth config: %v", err)) return } if r.Method != "GET" { http.Error(w, "Expected a GET", 400) return } code := r.FormValue("code") if code == "" { http.Error(w, "Expected a code", 400) return } // picago calls take an *http.Client, so we need to provide one which already // has a transport set up correctly wrt to authentication. In particular, it // needs to have the access token that is obtained during Exchange. transport := &oauth.Transport{ Config: oauthConfig, Transport: notOAuthTransport(ctx.HTTPClient()), } token, err := transport.Exchange(code) log.Printf("Token = %#v, error %v", token, err) if err != nil { log.Printf("Token Exchange error: %v", err) httputil.ServeError(w, r, fmt.Errorf("token exchange error: %v", err)) return } picagoCtx := ctx.Context.New(context.WithHTTPClient(transport.Client())) defer picagoCtx.Cancel() userInfo, err := im.getUserInfo(picagoCtx) if err != nil { log.Printf("Couldn't get username: %v", err) httputil.ServeError(w, r, fmt.Errorf("can't get username: %v", err)) return } if err := ctx.AccountNode.SetAttrs( importer.AcctAttrUserID, userInfo.ID, importer.AcctAttrGivenName, userInfo.FirstName, importer.AcctAttrFamilyName, userInfo.LastName, acctAttrOAuthToken, encodeToken(token), ); err != nil { httputil.ServeError(w, r, fmt.Errorf("Error setting attribute: %v", err)) return } http.Redirect(w, r, ctx.AccountURL(), http.StatusFound) }
// CreateAccount creates a new importer account for the Host h, and the importer // implementation named impl. It returns a RunContext setup with that account. func CreateAccount(h *Host, impl string) (*RunContext, error) { imp, ok := h.imp[impl] if !ok { return nil, fmt.Errorf("host does not have a %v importer", impl) } ia, err := imp.newAccount() if err != nil { return nil, fmt.Errorf("could not create new account for importer %v: %v", impl, err) } return &RunContext{ // TODO: context plumbing Context: context.New(context.WithHTTPClient(ia.im.host.HTTPClient())), Host: ia.im.host, ia: ia, }, nil }
func (imp) Run(ctx *importer.RunContext) error { clientId, secret, err := ctx.Credentials() if err != nil { return err } acctNode := ctx.AccountNode() ocfg := baseOAuthConfig ocfg.ClientId, ocfg.ClientSecret = clientId, secret token := decodeToken(acctNode.Attr(acctAttrOAuthToken)) transport := &oauth.Transport{ Config: &ocfg, Token: &token, Transport: notOAuthTransport(ctx.HTTPClient()), } ctx.Context = ctx.Context.New(context.WithHTTPClient(transport.Client())) root := ctx.RootNode() if root.Attr(nodeattr.Title) == "" { if err := root.SetAttr(nodeattr.Title, fmt.Sprintf("%s %s - Google/Picasa Photos", acctNode.Attr(importer.AcctAttrGivenName), acctNode.Attr(importer.AcctAttrFamilyName))); err != nil { return err } } r := &run{ RunContext: ctx, incremental: !forceFullImport && acctNode.Attr(importer.AcctAttrCompletedVersion) == runCompleteVersion, photoGate: syncutil.NewGate(3), } if err := r.importAlbums(); err != nil { return err } r.mu.Lock() anyErr := r.anyErr r.mu.Unlock() if !anyErr { if err := acctNode.SetAttrs(importer.AcctAttrCompletedVersion, runCompleteVersion); err != nil { return err } } return nil }
func TestGetUserID(t *testing.T) { ctx := context.New(context.WithHTTPClient(&http.Client{ Transport: httputil.NewFakeTransport(map[string]func() *http.Response{ apiURL + userInfoAPIPath: httputil.FileResponder(filepath.FromSlash("testdata/verify_credentials-res.json")), }), })) defer ctx.Cancel() inf, err := getUserInfo(importer.OAuthContext{ctx, &oauth.Client{}, &oauth.Credentials{}}) if err != nil { t.Fatal(err) } want := userInfo{ ID: "2325935334", ScreenName: "lejatorn", Name: "Mathieu Lonjaret", } if inf != want { t.Errorf("user info = %+v; want %+v", inf, want) } }
func TestGetUserId(t *testing.T) { im := &imp{} ctx := context.New(context.WithHTTPClient(&http.Client{ Transport: httputil.NewFakeTransport(map[string]func() *http.Response{ "https://api.foursquare.com/v2/users/self?oauth_token=footoken&v=20140225": httputil.FileResponder("testdata/users-me-res.json"), }), })) defer ctx.Cancel() inf, err := im.getUserInfo(ctx, "footoken") if err != nil { t.Fatal(err) } want := user{ Id: "13674", FirstName: "Brad", LastName: "Fitzpatrick", } if inf != want { t.Errorf("user info = %+v; want %+v", inf, want) } }
func TestGetUserId(t *testing.T) { userID := "11047045264" responder := httputil.FileResponder("testdata/users-me-res.xml") ctx := context.New(context.WithHTTPClient(&http.Client{ Transport: httputil.NewFakeTransport(map[string]func() *http.Response{ "https://picasaweb.google.com/data/feed/api/user/default/contacts?kind=user": responder, "https://picasaweb.google.com/data/feed/api/user/" + userID + "/contacts?kind=user": responder, }), })) defer ctx.Cancel() inf, err := picago.GetUser(ctx.HTTPClient(), "default") if err != nil { t.Fatal(err) } want := picago.User{ ID: userID, URI: "https://picasaweb.google.com/" + userID, Name: "Tamás Gulácsi", Thumbnail: "https://lh4.googleusercontent.com/-qqove344/AAAAAAAAAAI/AAAAAAABcbg/TXl3f2K9dzI/s64-c/11047045264.jpg", } if inf != want { t.Errorf("user info = %+v; want %+v", inf, want) } }
func newGeocodeContext() *context.Context { url := "https://maps.googleapis.com/maps/api/geocode/json?address=Uitdam&sensor=false" transport := httputil.NewFakeTransport(map[string]func() *http.Response{url: httputil.StaticResponder(uitdamGoogle)}) return context.New(context.WithHTTPClient(&http.Client{Transport: transport})) }