Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/remote-command #87

Merged
merged 4 commits into from
Dec 6, 2022
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
30 changes: 30 additions & 0 deletions assets/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,33 @@ var MoveNotesPrompt = &survey.Confirm{
Help: "Do you wanna move old notes to new path?",
Default: false,
}

// FirebaseRemoteConnectPromptQuestion is a question list that fills up
// required values for firebase remote connection.
// Used in Remote command's connect subcommand.
var FirebaseRemoteConnectPromptQuestion = []*survey.Question{
{
Name: "fire_project_id",
Prompt: &survey.Input{
Message: "Firebase Project ID",
Help: "The project ID of your Firebase project.",
},
Validate: survey.MinLength(1),
},
{
Name: "fire_account_key",
Prompt: &survey.Input{
Message: "Firebase Account Key",
Help: "The Firebase Admin SDK private key file path. Must be given a full path, like: /Users/john-doe/notya/account_key.json.",
},
Validate: survey.MinLength(5),
},
{
Name: "fire_collection",
Prompt: &survey.Input{
Message: "Firebase Collection",
Help: "A name of collection for notes, from your firebase project's firestore.",
},
Validate: survey.MinLength(1),
},
}
4 changes: 2 additions & 2 deletions lib/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func setupLocalService() {
loading.Start()

localService = services.NewLocalService(stdargs)
err := localService.Init()
err := localService.Init(nil)

loading.Stop()

Expand All @@ -135,7 +135,7 @@ func setupFirebaseService() {
loading.Start()

fireService = services.NewFirebaseService(stdargs, localService)
err := fireService.Init()
err := fireService.Init(nil)

loading.Stop()

Expand Down
2 changes: 1 addition & 1 deletion lib/commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func runInitCommand(cmd *cobra.Command, args []string) {
determineService()

loading.Start()
err := service.Init()
err := service.Init(nil)
loading.Stop()

if err != nil {
Expand Down
114 changes: 110 additions & 4 deletions lib/commands/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
package commands

import (
"fmt"

"github.com/AlecAivazis/survey/v2"
"github.com/fatih/color"
"github.com/insolite-dev/notya/assets"
"github.com/insolite-dev/notya/lib/models"
"github.com/insolite-dev/notya/lib/services"
"github.com/insolite-dev/notya/pkg"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -45,20 +53,118 @@ func initRemoteCommand() {
func runRemoteCommand(cmd *cobra.Command, args []string) {
determineService()

// TODO: list all active remote services.
loading.Start()
enabled, disabled := listAllRemote()
loading.Stop()

if len(enabled) > 0 {
pkg.Print("\nConnected Services:", color.FgGreen)
pkg.PrintServices(pkg.NOCOLOR, enabled)
}

if len(disabled) > 0 {
pkg.Print("\nUnreachable Services:", color.FgYellow)
pkg.PrintServices(pkg.NOCOLOR, disabled)
}
}

// runRemoteConnectCommand connects to a new remote service connection.
func runRemoteConnectCommand(cmd *cobra.Command, args []string) {
determineService()

// TODO: add survey to add new remote service.
// details: https://github.com/insolite-dev/notya/issues/83
_, disabled := listAllRemote()
loading.Stop()

if len(disabled) == 0 {
pkg.Alert(pkg.InfoL, "All remote service options are currently connected. You cannot establish additional connections at this time.")
return
}

// Ask for service selection.
var selected string
survey.AskOne(
assets.ChooseRemotePrompt(disabled),
&selected,
)

switch selected {
case services.FIRE.ToStr():
promptResult := models.Settings{}

// Ask for firebase prompt filling.
survey.Ask(assets.FirebaseRemoteConnectPromptQuestion, &promptResult)

loading.Start()

s := service.StateConfig()
updatedS := s.CopyWith(nil, nil, nil, nil, &promptResult.FirebaseProjectID, &promptResult.FirebaseAccountKey, &promptResult.FirebaseCollection)

// Validate provided firebase connection:
isEnabled := services.IsFirebaseEnabled(updatedS, &localService)

loading.Stop()

if !isEnabled {
pkg.Alert(pkg.ErrorL, "Unable to connect to the specified Firebase project using the provided credentials. Please check your login details and try again.")
return
}

loading.Start()
service.WriteSettings(updatedS)
loading.Stop()
}

pkg.Alert(pkg.SuccessL, fmt.Sprintf("Successfully connected to the specified %s project.", selected))
}

// runRemoteDisconnectCommand removes connection from concrete remove service
func runRemoteDisconnectCommand(cmd *cobra.Command, args []string) {
determineService()

// TODO: add functionality to disconnect from remote service.
loading.Start()
enabled, _ := listAllRemote()
loading.Stop()

if len(enabled) == 0 {
pkg.Alert(pkg.InfoL, "There are no active remote connections to disconnect from")
return
}

// Ask for service selection.
var selected string
survey.AskOne(
assets.ChooseRemotePrompt(enabled),
&selected,
)

loading.Start()
switch selected {
case services.FIRE.ToStr():
empty := ("")
s := service.StateConfig()
service.WriteSettings(s.CopyWith(nil, nil, nil, nil, &empty, &empty, &empty))
}

loading.Stop()

pkg.Alert(pkg.SuccessL, fmt.Sprintf("Successfully disconnected from specified %s service", selected))
}

// Returns a list of all remote services by splitting them by their enabled or disabled level.
// first returned array includes "enabled" remote services, and second returned array includes "disabled" remote services.
func listAllRemote() ([]string, []string) {
allEnabled, allDisabled := []string{}, []string{}

for _, s := range services.RemoteServices {
switch s {
case services.FIRE.ToStr():
if services.IsFirebaseEnabled(service.StateConfig(), &localService) {
allEnabled = append(allEnabled, s)
} else {
allDisabled = append(allDisabled, s)
}
}
}

return allEnabled, allDisabled
}
44 changes: 39 additions & 5 deletions lib/models/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,45 @@ type Settings struct {
FirebaseCollection string `json:"fire_collection,omitempty" mapstructure:"fire_collection,omitempty" survey:"fire_collection"`
}

// CopyWith updates pointed settings with a new data.
// if given argument is not nil, it will be overwritten
// inside pointed settings model.
func (s *Settings) CopyWith(
ID *string,
Name *string,
Editor *string,
NotesPath *string,
FirebaseProjectID *string,
FirebaseAccountKey *string,
FirebaseCollection *string,
) Settings {
ss := *s

if ID != nil {
ss.ID = *ID
}
if Name != nil {
ss.Name = *Name
}
if Editor != nil {
ss.Editor = *Editor
}
if NotesPath != nil {
ss.NotesPath = *NotesPath
}
if FirebaseProjectID != nil {
ss.FirebaseProjectID = *FirebaseProjectID
}
if FirebaseAccountKey != nil {
ss.FirebaseAccountKey = *FirebaseAccountKey
}
if FirebaseCollection != nil {
ss.FirebaseCollection = *FirebaseCollection
}

return ss
}

// InitSettings returns default variant of settings structure model.
func InitSettings(notesPath string) Settings {
return Settings{
Expand Down Expand Up @@ -137,8 +176,3 @@ func (s *Settings) FirePath() string {
func (s *Settings) IsValid() bool {
return len(s.Name) > 0 && len(s.Editor) > 0 && len(s.NotesPath) > 0
}

// isFirebaseEnabled checks the validness of firebase fields.
func (s *Settings) IsFirebaseEnabled() bool {
return len(s.FirebaseProjectID) > 0 || len(s.FirebaseAccountKey) > 0 || len(s.FirebaseCollection) > 0
}
24 changes: 0 additions & 24 deletions lib/models/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,27 +175,3 @@ func TestIsValid(t *testing.T) {
})
}
}

func TestIsFirebaseEnabled(t *testing.T) {
tests := []struct {
settings models.Settings
expected bool
}{
{
settings: models.InitSettings("/usr/mock/NotesPath"),
expected: false,
},
{
settings: models.Settings{FirebaseProjectID: "mock-project-id"},
expected: true,
},
}

for _, td := range tests {
got := td.settings.IsFirebaseEnabled()

if got != td.expected {
t.Errorf("IsFirebaseEnabled sum was different: Want: %v | Got: %v", got, td.expected)
}
}
}
17 changes: 11 additions & 6 deletions lib/services/firebase_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,17 @@ func (s *FirebaseService) Path() (string, string) {
}

// Init creates notya working directory into current machine.
func (s *FirebaseService) Init() error {
localConfig, err := s.LS.Settings(nil)
if err != nil {
return err
func (s *FirebaseService) Init(settings *models.Settings) error {
if settings != nil {
s.Config = *settings
} else {
localConfig, err := s.LS.Settings(nil)
if err != nil {
return err
}

s.Config = *localConfig // should be re-written later.
}
s.Config = *localConfig // should be re-written later.

if len(s.Config.FirebaseProjectID) == 0 {
return assets.InvalidFirebaseProjectID
Expand All @@ -115,7 +120,7 @@ func (s *FirebaseService) Init() error {

config, err := s.Settings(nil)
if status.Code(err) == codes.NotFound {
if err := s.WriteSettings(*localConfig); err != nil {
if err := s.WriteSettings(s.Config); err != nil {
return err
}
} else if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion lib/services/local_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (l *LocalService) StateConfig() models.Settings {
}

// Init creates notya working directory into current machine.
func (l *LocalService) Init() error {
func (l *LocalService) Init(settings *models.Settings) error {
notyaPath, err := pkg.NotyaPWD(l.Config)
if err != nil {
pkg.Alert(pkg.ErrorL, err.Error())
Expand Down
20 changes: 18 additions & 2 deletions lib/services/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@

package services

import "github.com/insolite-dev/notya/lib/models"
import (
"os"

"github.com/insolite-dev/notya/lib/models"
)

var (
LOCAL ServiceType = "LOCAL"
FIRE ServiceType = "FIREBASE"

// All services into one list: including local and remote.
Services []string = []string{
LOCAL.ToStr(),
FIRE.ToStr(),
}

// Only remote services into one list.
RemoteServices []string = []string{FIRE.ToStr()}
)

// Custom string struct to define type of services
Expand All @@ -33,6 +41,14 @@ func (s *ServiceType) ToStr() string {
return "undefined"
}

// IsFirebaseEnabled checks if firebase connection is enabled or not.
func IsFirebaseEnabled(s models.Settings, local *ServiceRepo) bool {
stargs := models.StdArgs{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr}
err := NewFirebaseService(stargs, *local).Init(&s)

return err == nil
}

// ServiceRepo is a abstract class for all service implementations.
// ╭──────╮ ╭────────────────────╮
// ... │ User │ ──▶ │ Interface Commands │
Expand Down Expand Up @@ -63,7 +79,7 @@ type ServiceRepo interface {
StateConfig() models.Settings

// Init setups all kinda minimal services for application.
Init() error
Init(settings *models.Settings) error

// Settings reads and parses current configuration file and returns
// it as settings model pointer. In case of a error, setting model will be
Expand Down
10 changes: 9 additions & 1 deletion pkg/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func PrintSettings(settings models.Settings) {
func PrintErrors(act string, errs []error) {
for i, e := range errs {
err := fmt.Sprintf("%v | %v",
fmt.Sprintf("%s%s%s", RED, fmt.Sprintf("- SWW fetch:%v", i+1), NOCOLOR),
fmt.Sprintf("%s%s%s", RED, fmt.Sprintf("- SWW %s:%v", act, i+1), NOCOLOR),
e.Error(),
)

Expand All @@ -181,3 +181,11 @@ func Spinner() *spinner.Spinner {
s.Color("yellow")
return s
}

// PrintServices logs given service names by provided color level.
func PrintServices(c string, services []string) {
for _, s := range services {
printable := fmt.Sprintf(" • %s", fmt.Sprintf("%s%s%s", c, s, NOCOLOR))
text.Println(printable)
}
}