Skip to content

Commit 73f478f

Browse files
Merge pull request #2 from 01walid/master
Refactoring, Support links mode and some enhancements
2 parents 391fed9 + f70129b commit 73f478f

15 files changed

+1319
-787
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
*.csv
44
.idea
55
bin
6+
vendor/

Gopkg.lock

+81
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
# Gopkg.toml example
3+
#
4+
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
5+
# for detailed Gopkg.toml documentation.
6+
#
7+
# required = ["github.com/user/thing/cmd/thing"]
8+
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
9+
#
10+
# [[constraint]]
11+
# name = "github.com/user/project"
12+
# version = "1.0.0"
13+
#
14+
# [[constraint]]
15+
# name = "github.com/user/project2"
16+
# branch = "dev"
17+
# source = "github.com/myfork/project2"
18+
#
19+
# [[override]]
20+
# name = "github.com/x/y"
21+
# version = "2.4.0"
22+
23+
24+
[[constraint]]
25+
branch = "master"
26+
name = "github.com/spf13/cobra"
27+
28+
[[constraint]]
29+
name = "github.com/stretchr/testify"
30+
version = "1.1.4"
31+
32+
[[constraint]]
33+
name = "github.com/fatih/color"
34+
version = "1.5.0"
35+
36+
[[constraint]]
37+
name = "gopkg.in/cheggaaa/pb.v1"
38+
version = "1.0.18"

cmd/colors.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/fatih/color"
7+
)
8+
9+
// CError a Red-colored Error string (with Ansi escape codes, supports Windows)
10+
func CError(format string, a ...interface{}) error {
11+
red := color.New(color.FgHiRed).SprintfFunc()
12+
// Append a new line for a better error readibility.
13+
return fmt.Errorf(red(format+"\n"), a...)
14+
}
15+
16+
// PrintRed prints a red text into the terminal
17+
func PrintRed(format string, a ...interface{}) {
18+
color.Red(format, a...)
19+
}
20+
21+
// PrintYellow prints a yellow text into the terminal
22+
func PrintYellow(format string, a ...interface{}) {
23+
color.Yellow(format, a...)
24+
}
25+
26+
// StringYellow format a text with yellow ansi escape codes
27+
func StringYellow(text string) string {
28+
yellow := color.New(color.FgHiYellow).SprintfFunc()
29+
return yellow(text)
30+
}

cmd/flags.go

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
)
10+
11+
// Command Line flags
12+
var (
13+
username string // Username for Audisto API authentication
14+
password string // Password for audisto API authentication
15+
crawlID uint64 // ID of the crawl to download
16+
chunkNumber uint64 // Number of Chunk
17+
chunkSize uint64 // Elements in each chunk
18+
output string // Output format
19+
filter string // Possible filter
20+
noResume bool // Resume or not any previously downloaded file
21+
noDetails bool // Request or not details from Audisto API
22+
order string // Possible order of results
23+
mode string // pages or links
24+
targets string // "self" or a path to a file containing link target pages (IDs)
25+
)
26+
27+
// register global flags that apply to the root command
28+
func registerPersistentFlags(rootCmd *cobra.Command) {
29+
pf := rootCmd.PersistentFlags()
30+
pf.StringVarP(&username, "username", "u", "", "Audisto API Username (required)")
31+
pf.StringVarP(&password, "password", "p", "", "Audisto API Password (required)")
32+
pf.Uint64VarP(&crawlID, "crawl", "c", 0, "ID of the crawl to download (required)")
33+
pf.StringVarP(&mode, "mode", "m", "pages", "Download mode, set it to 'links' or 'pages' (default)")
34+
pf.BoolVarP(&noDetails, "no-details", "d", false, "If passed, details in API request is set to 0")
35+
pf.StringVarP(&output, "output", "o", "", "Path for the output file")
36+
pf.BoolVarP(&noResume, "no-resume", "r", false, "If passed, download starts again, else the download is resumed")
37+
pf.StringVarP(&filter, "filter", "f", "", "Filter all pages by some attributes")
38+
pf.StringVarP(&order, "order", "", "", "Order by some attributes")
39+
pf.StringVarP(&targets, "targets", "t", "", `"self" or a path to a file containing link target pages (IDs)`)
40+
}
41+
42+
// check if --username --password and --crawl are being passed with non-empty values
43+
func requiredFlagsPassed() bool {
44+
return username != "" && password != "" && crawlID != 0
45+
}
46+
47+
// Beside parsing flags and auto-type inferring offered by Cobra package
48+
// we check for our own flag validations/logic as well
49+
func customFlagsValidation(cmd *cobra.Command) error {
50+
51+
// make sure required flags are passed
52+
if !requiredFlagsPassed() {
53+
return CError("--username, --password and --crawl are required")
54+
}
55+
56+
// normalize flags before proceeding with the validation
57+
normalizeFlags()
58+
59+
// validate mode
60+
if mode != "" && mode != "pages" && mode != "links" {
61+
msg := "mode has to be 'links' or 'pages', if this flag is dropped, it will default to 'pages'"
62+
return CError(msg)
63+
}
64+
65+
// validate targets / mode / filter combinations
66+
if targets != "" {
67+
68+
// do not allow --filter when --targets is being used
69+
if filter != "" {
70+
return CError("Set either --filter or --targets, but not both")
71+
}
72+
73+
// --mode=pages is only allowed when targets=self
74+
if targets == "self" && mode != "pages" {
75+
return CError("Set --mode=pages to use --targets=self")
76+
}
77+
78+
// --targets=FILEPATH is only allowed when mode is set to links
79+
// we'd also make sure the file exists.
80+
if targets != "self" {
81+
82+
if mode != "links" {
83+
return CError("Set --mode=links to use --targets=FILEPATH")
84+
}
85+
86+
if _, err := os.Stat(targets); os.IsNotExist(err) {
87+
return CError(fmt.Sprintf("%s file does not exist", targets))
88+
}
89+
}
90+
91+
}
92+
// returning no error means the validation passed
93+
return nil
94+
}
95+
96+
// trim spaces and lowercase [some] string-based flags
97+
func normalizeFlags() {
98+
99+
// trim spaces for 'mode', 'targets', 'output', 'filter' and 'order'
100+
mode = strings.TrimSpace(mode)
101+
targets = strings.TrimSpace(targets)
102+
output = strings.TrimSpace(output)
103+
filter = strings.TrimSpace(filter)
104+
order = strings.TrimSpace(order)
105+
106+
// lowercase 'mode'
107+
mode = strings.ToLower(mode)
108+
109+
// lowercase 'targets' when it's being set to 'self'
110+
if strings.EqualFold(targets, "self") {
111+
targets = "self"
112+
}
113+
}

cmd/root.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package cmd
2+
3+
import (
4+
"github.com/audisto/data-downloader/downloader"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
// RootCmd the root (default) command to data-downloader
9+
var RootCmd = &cobra.Command{
10+
Use: "data-downloader",
11+
Short: "Audisto Data Downloader",
12+
Long: "A simple CLI tool to download data using Audisto API",
13+
RunE: func(cmd *cobra.Command, args []string) error {
14+
// Run our custom flags validation
15+
err := customFlagsValidation(cmd)
16+
if err != nil {
17+
return err
18+
}
19+
return performDownload()
20+
},
21+
Example: getExamples(),
22+
}
23+
24+
func init() {
25+
// register global flags that apply to the root command
26+
registerPersistentFlags(RootCmd)
27+
}
28+
29+
// use Audisto downloader package to initiate/resume API downloads
30+
func performDownload() error {
31+
return downloader.Get(username, password, crawlID, mode, noDetails,
32+
chunkNumber, chunkSize, output, filter, noResume, order)
33+
}
34+
35+
// example command usage hooked into the CLI usage text.
36+
func getExamples() string {
37+
// Todo: change color
38+
return StringYellow(`
39+
$ data-downloader --username="USERNAME" --password="PASSWORD" --crawl=12345 --output="myCrawl.tsv"
40+
$ data-downloader -u="USERNAME" -p="PASSWORD" -c=12345 -o="myCrawl.tsv" --no-resume -m=links
41+
`)
42+
}

cmd/version.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
// VERSION Audisto data downloader version number
10+
const VERSION = "0.4"
11+
12+
func init() {
13+
RootCmd.AddCommand(versionCmd)
14+
}
15+
16+
var versionCmd = &cobra.Command{
17+
Use: "version",
18+
Short: "Print the version number of data-downloader",
19+
Long: `Print the version number of data-downloader`,
20+
Run: func(cmd *cobra.Command, args []string) {
21+
fmt.Println("data-downloader v" + VERSION)
22+
},
23+
}

0 commit comments

Comments
 (0)