csv-curl 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/README.md +54 -0
  4. data/bin/csv-curl +47 -18
  5. data/csv-curl.gemspec +2 -2
  6. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d04f4437667318ac6a0d869e1ebbc6b72de7d6d1c86ecd55b98dd8d74ae79a5b
4
- data.tar.gz: 944c0ef6d3837bbd38e4d1bd11b4e3d36fab95274452cb121f9440e34b0f9748
3
+ metadata.gz: 381850913b0f63297ab7a5186c9108880d09774930604bd965f7958b573bdeca
4
+ data.tar.gz: df854cee03e5e4f2c1bc68da0d1c37a845ab8ad9fcb9b19aa0c8f024064999c1
5
5
  SHA512:
6
- metadata.gz: c432715c775b43c27f38b6e21b9410a934ce87a528a17c32f86ac906de98185d17e3e84bcb700dab4f767d7fae4f0dd3df4eda5cdb475e200e785db5f5fa0b01
7
- data.tar.gz: db278224c3a4e4d1f702de80495f4758796916ac69ca363cebe9c94cd580948ec8a3adae0315994f4d62f3c5126831519ddac915cc21fabf59b43aab9a847035
6
+ metadata.gz: 9582c3888f042d002b1d229d5433c2bfa7c9d4e8cf7845d3caed560219614f393eca5fa659c8f2f286c7222ebcee4b53bbdccbe576356998a2d79df89cd6a755
7
+ data.tar.gz: c45dd8f7fc333b38b493b2b4904956b678c498c7fa281f41b90a6d7960e3037ab5bdd3732c2a366087e949d3673de0f69aed6ce21e0be18e3bacba443c6109f1
data/.gitignore CHANGED
@@ -50,3 +50,6 @@ build-iPhoneSimulator/
50
50
  .rvmrc
51
51
 
52
52
  *~
53
+
54
+ csv-utils.response
55
+ csv-utils.request
data/README.md CHANGED
@@ -1 +1,55 @@
1
1
  # csv-curl
2
+
3
+ Utility for making multiple curl requests using a CSV file.
4
+
5
+ ### Usage
6
+ ```
7
+ Usage: csv-curl[OPTIONS] [CURL OPTIONS]
8
+ --template FILE Path to the template file
9
+ --csv CSV_FILE Path to the CSV file
10
+ --exec UTILITY Utility to run on each response
11
+ ```
12
+
13
+ ### Template
14
+
15
+ File containing the payload to pass to the curl command, the "-d@<file>" option.
16
+
17
+ ### CSV File
18
+
19
+ A CSV file containing the replacement values to use. It must have a header. And have a header for all replacement variables.
20
+
21
+ ### Utility
22
+
23
+ This is an additional command to run on the response body from the curl command.
24
+
25
+ Example to output only the "id" from a JSON body
26
+
27
+ ```
28
+ csv-curl --csv emails.csv 'https://example.com/user_lookup?email={{param:email}}' --exec 'jq ".id" -r'
29
+ ```
30
+
31
+ ### Replacement variables
32
+
33
+ A replacement variable is enclosed in handlebars **{{** **format:** **name** **}}**. Replacement variables can be used in the arguments to curl, in the template and in the utility.
34
+
35
+ *(format and name a separated by a colon)*
36
+
37
+ - **name** matches a header in the CSV file
38
+ - **format** escape sequence to apply the the replacement value
39
+ - _json_ JSON encode
40
+ - _param_ URL encode
41
+ - _base64_ Base64 encode
42
+ - _urlsafe64_ URL safe Base64 encode
43
+ - _hex_ Hex encode
44
+ - _shell_ Shell encode
45
+
46
+ Example: lookup users by email and save response by user id.
47
+
48
+ ```
49
+ csv-curl --csv users.csv 'https://example.com/user_lookup?email={{param:email}}' --exec 'cat > {{id}}.json'
50
+
51
+ # users.csv
52
+ id,email
53
+ 5,bob@example.com
54
+ 8,johndoe@example.com
55
+ ```
@@ -11,30 +11,51 @@ require 'digest/md5'
11
11
  options = {
12
12
  template_file: nil,
13
13
  csv_file: nil,
14
- response_filter: nil,
14
+ exec: nil,
15
15
  default_format: 'json'
16
16
  }
17
17
 
18
- OptionParser.new do |opts|
19
- opts.banner = "Usage: " + File.basename(__FILE__) + " [CURL OPTIONS]"
18
+ OPTPARSE = OptionParser.new do |opts|
19
+ opts.banner = "Usage: " + File.basename(__FILE__) + "[OPTIONS] [CURL OPTIONS]"
20
20
 
21
- opts.on('--template REQUEST', 'Request template') do |v|
21
+ opts.on('--template FILE', 'Path to the template file') do |v|
22
22
  options[:template_file] = v
23
23
  end
24
24
 
25
- opts.on('--csv CSV_FILE', 'Request data') do |v|
25
+ opts.on('--csv CSV_FILE', 'Path to the CSV file') do |v|
26
26
  options[:csv_file] = v
27
27
  end
28
28
 
29
- opts.on('--response-filter COMMAND', 'Command to run on each response') do |v|
30
- options[:response_filter] = v
29
+ opts.on('--exec UTILITY', 'Utility to run on each response') do |v|
30
+ options[:exec] = v
31
31
  end
32
- end.parse!
32
+ end
33
+
34
+ CSV_CURL_ARGV = []
35
+ ARGV.size.times.to_a.reverse.each do |idx|
36
+ case ARGV[idx]
37
+ when '--template',
38
+ '--csv',
39
+ '--exec'
40
+ CSV_CURL_ARGV << ARGV.delete_at(idx)
41
+ CSV_CURL_ARGV << ARGV.delete_at(idx)
42
+ end
43
+ end
44
+ OPTPARSE.parse(CSV_CURL_ARGV)
45
+
46
+ def failed(msg, errno = 1)
47
+ $stderr.puts msg
48
+ if errno == 1
49
+ puts ""
50
+ puts OPTPARSE
51
+ end
52
+ exit errno
53
+ end
33
54
 
34
- raise('no csv input file specified') unless options[:csv_file]
35
- raise("csv input file #{options[:csv_file]} not found") unless File.exist?(options[:csv_file])
55
+ failed('no csv input file specified') unless options[:csv_file]
56
+ failed("csv input file #{options[:csv_file]} not found") unless File.exist?(options[:csv_file])
36
57
 
37
- raise("template file #{options[:template_file]} not found") if options[:template_file] && !File.exist?(options[:template_file])
58
+ failed("template file #{options[:template_file]} not found") if options[:template_file] && !File.exist?(options[:template_file])
38
59
 
39
60
  template = options[:template_file] ? File.read(options[:template_file]) : nil
40
61
  input = CSV.open(options[:csv_file])
@@ -44,18 +65,20 @@ required_replacements = ARGV.map do |arg|
44
65
  arg.scan(/\{\{(.*?)\}\}/)
45
66
  end
46
67
 
47
- required_replacements += options[:response_filter].scan(/\{\{(.*?)\}\}/) if options[:response_filter]
68
+ required_replacements += options[:exec].scan(/\{\{(.*?)\}\}/) if options[:exec]
48
69
  required_replacements += template.scan(/\{\{(.*?)\}\}/) if template
49
70
  required_replacements.flatten!
50
71
  required_replacements.map! { |v| v.split(':', 2).last }
51
72
  required_replacements.uniq!
52
73
 
53
74
  missing_replacements = required_replacements - input_headers
54
- raise("missing replacement values for #{missing_replacements.join(', ')}") unless missing_replacements.empty?
75
+ failed("missing replacement values for #{missing_replacements.join(', ')}", 2) unless missing_replacements.empty?
55
76
 
56
77
  TMP_REQUEST_BODY_FILE = 'csv-utils.request'
57
78
 
58
79
  def format_value(value, format)
80
+ return '' unless value
81
+
59
82
  case format
60
83
  when 'json'
61
84
  value = value.to_json
@@ -67,6 +90,8 @@ def format_value(value, format)
67
90
  CGI.escape(value)
68
91
  when 'base64'
69
92
  Base64.strict_encode64(value)
93
+ when 'urlsafe64'
94
+ Base64.urlsafe_encode64(value)
70
95
  when 'hex'
71
96
  value.unpack('H*').first
72
97
  when 'shellword',
@@ -92,7 +117,7 @@ end
92
117
 
93
118
  def run_command_safely(cmd)
94
119
  res = `#{cmd}`
95
- raise("failed to run command: #{cmd}") unless $?.success?
120
+ failed("failed to run command: #{cmd}", 2) unless $?.success?
96
121
  res
97
122
  end
98
123
 
@@ -104,9 +129,9 @@ def build_curl_command(curl_args, data, request_file, response_file)
104
129
  cmd
105
130
  end
106
131
 
107
- def build_response_filter_command(response_filter, data, response_file)
132
+ def build_exec_command(exec, data, response_file)
108
133
  cmd = "cat #{response_file} | "
109
- cmd += generate_string(response_filter, data)
134
+ cmd += generate_string(exec, data)
110
135
  cmd
111
136
  end
112
137
 
@@ -128,12 +153,16 @@ while (row = input.shift)
128
153
  run_command_safely(cmd)
129
154
 
130
155
  output =
131
- if options[:response_filter]
132
- cmd = build_response_filter_command(options[:response_filter], data, response_file)
156
+ if options[:exec]
157
+ cmd = build_exec_command(options[:exec], data, response_file)
133
158
  run_command_safely(cmd)
134
159
  else
135
160
  File.read(response_file)
136
161
  end
137
162
 
138
163
  puts output
164
+
165
+ File.unlink(response_file) if File.exist?(response_file)
139
166
  end
167
+
168
+ File.unlink(TMP_REQUEST_BODY_FILE) if File.exist?(TMP_REQUEST_BODY_FILE)
@@ -2,14 +2,14 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'csv-curl'
5
- s.version = '0.1.0'
5
+ s.version = '0.2.0'
6
6
  s.licenses = ['MIT']
7
7
  s.summary = 'CSV Curl'
8
8
  s.description = 'Tools making mulitple calls using curl'
9
9
  s.authors = ['Doug Youch']
10
10
  s.email = 'dougyouch@gmail.com'
11
11
  s.homepage = 'https://github.com/dougyouch/csv-curl'
12
- s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
12
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples)/}) }
13
13
  s.bindir = 'bin'
14
14
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv-curl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doug Youch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-22 00:00:00.000000000 Z
11
+ date: 2019-08-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Tools making mulitple calls using curl
14
14
  email: dougyouch@gmail.com