Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions cmd/tick/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ func resolveRegion() (string, error) {
return cfg.Region, nil
}

func buildAuthService(region string, streams cli.Streams) (auth.Service, error) {
endpoints, err := endpoint.ForRegion(region)
if err != nil {
return auth.Service{}, err
}
return auth.Service{
AuthorizeURL: endpoints.AuthorizeURL,
Exchanger: auth.Exchanger{
TokenURL: endpoints.TokenURL,
},
Store: auth.FileStore{},
Browser: browserOpener{},
In: streams.In,
Out: streams.Out,
}, nil
}

func buildRuntime(streams cli.Streams) (*config.Store, auth.Service, *ticktick.Client, error) {
store, cfg, err := loadConfigStore()
if err != nil {
Expand Down Expand Up @@ -76,13 +93,15 @@ func main() {
Streams: streams,
RegionResolver: resolveRegion,
LoginAuthResolver: func() (*app.AuthApp, error) {
store, service, _, err := buildRuntime(streams)
store, _, err := loadConfigStore()
if err != nil {
return nil, err
}
return &app.AuthApp{
ConfigStore: store,
Service: service,
ServiceForRegion: func(region string) (app.AuthService, error) {
return buildAuthService(region, streams)
},
}, nil
},
AuthServiceResolver: func() (app.AuthService, error) {
Expand Down
20 changes: 17 additions & 3 deletions internal/app/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ type AuthService interface {
Logout(context.Context) error
}

type AuthServiceFactory func(region string) (AuthService, error)

type AuthApp struct {
ConfigStore *config.Store
Service AuthService
ConfigStore *config.Store
Service AuthService
ServiceForRegion AuthServiceFactory
}

type LoginInput struct {
Expand Down Expand Up @@ -58,6 +61,17 @@ func (a AuthApp) Login(ctx context.Context, in LoginInput) error {
if in.ClientID == "" || in.ClientSecret == "" || in.RedirectURL == "" {
return errors.New("login requires client-id, client-secret, and redirect-url")
}
service := a.Service
if a.ServiceForRegion != nil {
var err error
service, err = a.ServiceForRegion(in.Region)
if err != nil {
return err
}
}
if service == nil {
return errors.New("auth service is unavailable")
}
if a.ConfigStore != nil {
cfg.ClientID = in.ClientID
cfg.ClientSecret = in.ClientSecret
Expand All @@ -67,7 +81,7 @@ func (a AuthApp) Login(ctx context.Context, in LoginInput) error {
return err
}
}
_, err := a.Service.Login(ctx, auth.LoginInput{
_, err := service.Login(ctx, auth.LoginInput{
ClientID: in.ClientID,
ClientSecret: in.ClientSecret,
RedirectURL: in.RedirectURL,
Expand Down
60 changes: 60 additions & 0 deletions internal/app/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ func (f fakeAuthService) Logout(context.Context) error {
return nil
}

type recordingLoginAuthService struct {
loginInput auth.LoginInput
}

func (r *recordingLoginAuthService) Login(_ context.Context, in auth.LoginInput) (auth.Token, error) {
r.loginInput = in
return auth.Token{AccessToken: "access-1"}, nil
}

func (r *recordingLoginAuthService) Status(context.Context) (auth.Status, error) {
return auth.Status{}, nil
}

func (r *recordingLoginAuthService) Logout(context.Context) error {
return nil
}

func TestAuthAppStatus(t *testing.T) {
store := config.NewStore(t.TempDir() + "/config.yaml")
app := AuthApp{
Expand Down Expand Up @@ -125,6 +142,49 @@ func TestAuthAppLoginPersistsConfigOnSuccess(t *testing.T) {
}
}

func TestAuthAppLoginBuildsServiceForSelectedRegion(t *testing.T) {
store := config.NewStore(t.TempDir() + "/config.yaml")
if err := store.Save(config.Config{
Region: "ticktick",
ClientID: "old-client-id",
ClientSecret: "old-secret",
RedirectURL: "http://localhost:14573/callback",
}); err != nil {
t.Fatalf("Save() error = %v", err)
}

selectedRegion := ""
service := &recordingLoginAuthService{}
app := AuthApp{
ConfigStore: store,
ServiceForRegion: func(region string) (AuthService, error) {
selectedRegion = region
return service, nil
},
}

if err := app.Login(context.Background(), LoginInput{
Region: "dida365",
ClientID: "new-client-id",
ClientSecret: "new-secret",
}); err != nil {
t.Fatalf("Login() error = %v", err)
}
if selectedRegion != "dida365" {
t.Fatalf("service region = %q, want dida365", selectedRegion)
}
if service.loginInput.ClientID != "new-client-id" {
t.Fatalf("ClientID = %q, want new-client-id", service.loginInput.ClientID)
}
cfg, err := store.Load()
if err != nil {
t.Fatalf("Load() error = %v", err)
}
if cfg.Region != "dida365" {
t.Fatalf("stored Region = %q, want dida365", cfg.Region)
}
}

func TestAuthAppLoginSucceedsWithoutConfigStoreWhenInputsAreExplicit(t *testing.T) {
app := AuthApp{
Service: fakeAuthService{},
Expand Down
Loading