apigee_cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,109 @@
1
+ module ApigeeCli
2
+ class ConfigSet < Base
3
+
4
+ MAP_KEY = 'keyValueMap'
5
+ ENTRY_KEY = 'entry'
6
+ DEFAULT_CONFIG_NAME = 'configuration'
7
+
8
+ def base_url
9
+ "https://api.enterprise.apigee.com/v1/o/#{org}/environments/#{environment}/keyvaluemaps"
10
+ end
11
+
12
+ def list_configs
13
+ # We need the expand: true option to get an expanded view of the KeyValueMaps
14
+ response = get(base_url, expand: true)
15
+ if response.status != 200
16
+ response_error(response)
17
+ else
18
+ JSON.parse(response.body)
19
+ end
20
+ end
21
+
22
+ def read_config(config_name)
23
+ url = [base_url, config_name].join('/')
24
+ response = get(url)
25
+ if response.status != 200
26
+ response_error(response)
27
+ else
28
+ JSON.parse(response.body)
29
+ end
30
+ end
31
+
32
+ def write_config(config_name, data)
33
+ body = {
34
+ name: config_name,
35
+ entry: data
36
+ }
37
+ response = post(base_url, body)
38
+ if response.status != 201
39
+ response_error(response)
40
+ else
41
+ JSON.parse(response.body)
42
+ end
43
+ end
44
+
45
+ def update_config(config_name, data)
46
+ url = [base_url, config_name].join('/')
47
+ body = {
48
+ name: config_name,
49
+ entry: data
50
+ }
51
+ response = put(url, body)
52
+ if response.status != 200
53
+ response_error(response)
54
+ else
55
+ JSON.parse(response.body)
56
+ end
57
+ end
58
+
59
+ def add_config(config_name, data, overwrite)
60
+ changed_keys = []
61
+
62
+ begin
63
+ response = Hashie::Mash.new(read_config(config_name))
64
+ if overwrite
65
+ result = :overwritten
66
+ else
67
+ orig_keys = response[ENTRY_KEY].map(&:name)
68
+ data.reject! { |pair| orig_keys.include?(pair['name']) }
69
+
70
+ result = :existing
71
+ end
72
+
73
+ update_config(config_name, data)
74
+ rescue RuntimeError => e
75
+ if e.message.include?('keyvaluemap_doesnt_exist')
76
+ result = :new
77
+ write_config(config_name, data)
78
+ else
79
+ changed_keys = [e.to_s]
80
+ result = :error
81
+ end
82
+ end
83
+
84
+ changed_keys = data.map(&:name)
85
+ [result, changed_keys]
86
+ end
87
+
88
+ def remove_config(config_name)
89
+ url = [base_url, config_name].join('/')
90
+ response = delete(url)
91
+ if response.status != 200
92
+ response_error(response)
93
+ else
94
+ JSON.parse(response.body)
95
+ end
96
+ end
97
+
98
+ def remove_entry(config_name, entry_name)
99
+ url = [base_url, config_name, 'entries', entry_name].join('/')
100
+
101
+ response = delete(url)
102
+ if response.status != 200
103
+ response_error(response)
104
+ else
105
+ JSON.parse(response.body)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,42 @@
1
+ module ApigeeCli
2
+ class << self
3
+ def configuration
4
+ @configuration ||= ApigeeCli::Configuration.new
5
+ end
6
+ end
7
+
8
+ class Configuration
9
+ def apigeerc_config
10
+ begin
11
+ @apigeerc_config = YAML.load_file("#{ENV['HOME']}/.apigeerc")
12
+ rescue
13
+ raise "Error loading .apigeerc file"
14
+ end
15
+ @apigeerc_config.merge! local_apigeerc_config
16
+ @apigeerc_config
17
+ end
18
+
19
+ def username
20
+ raise 'Not Configured' if apigeerc_config['username'].nil?
21
+ apigeerc_config['username']
22
+ end
23
+
24
+ def password
25
+ raise 'Not Configured' if apigeerc_config['password'].nil?
26
+ apigeerc_config['password']
27
+ end
28
+
29
+ def org
30
+ raise 'Not Configured' if apigeerc_config['org'].nil?
31
+ apigeerc_config['org']
32
+ end
33
+
34
+ def local_apigeerc_config
35
+ File.exists?("./.apigeerc") ? YAML.load_file("./.apigeerc") : {}
36
+ end
37
+
38
+ def method_missing(sym, *args, &block)
39
+ apigeerc_config[sym.to_s]
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ module ApigeeCli
2
+ class ResourceFile < Base
3
+
4
+ RESOURCE_FILE_KEY = 'resourceFile'
5
+ DEFAULT_RESOURCE_TYPE = 'jsc'
6
+
7
+ def base_url
8
+ "https://api.enterprise.apigee.com/v1/organizations/#{org}/resourcefiles"
9
+ end
10
+
11
+ def all
12
+ response = get(base_url)
13
+ if response.status != 200
14
+ response_error(response)
15
+ else
16
+ JSON.parse(response.body)
17
+ end
18
+ end
19
+
20
+ def read(name, resource_type)
21
+ url = [base_url,resource_type,name].join('/')
22
+ response = get(url)
23
+ if response.status == 404
24
+ nil
25
+ elsif response.status != 200
26
+ response_error(response)
27
+ else
28
+ response.body
29
+ end
30
+ end
31
+
32
+ def create(name, resource_type, file)
33
+ url = "#{base_url}?name=#{name}&type=#{resource_type}"
34
+ response = upload_file(url, file)
35
+ if response.status != 201
36
+ response_error(response)
37
+ else
38
+ JSON.parse(response.body)
39
+ end
40
+ end
41
+
42
+ def remove(name, resource_type)
43
+ url = [base_url,resource_type,name].join('/')
44
+ response = delete(url)
45
+ if response.status != 200
46
+ response_error(response)
47
+ else
48
+ JSON.parse(response.body)
49
+ end
50
+ end
51
+
52
+ def upload(name, resource_type, file)
53
+ if read(name, resource_type)
54
+ result = :overwritten
55
+ remove(name, resource_type)
56
+ else
57
+ result = :new_file
58
+ end
59
+ create(name, resource_type, file)
60
+ result
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module ApigeeCli
2
+ VERSION = "0.0.1"
3
+ end
data/lib/apigee_cli.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'httparty'
2
+ require 'hashie'
3
+ require 'git'
4
+
5
+ begin
6
+ require 'pry'
7
+ rescue Gem::LoadError
8
+ end
9
+
10
+ require 'apigee_cli/configuration'
11
+ require 'apigee_cli/base'
12
+ require 'apigee_cli/version'
13
+
14
+ require 'apigee_cli/config_set'
15
+ require 'apigee_cli/resource_file'
16
+
17
+ module ApigeeCli
18
+ end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'apigee_cli/base'
3
+
4
+ describe ApigeeCli::Base do
5
+ before do
6
+ module ApigeeCli
7
+ class Foo < Base
8
+ def base_url
9
+ "http://foos.com"
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:foo) { ApigeeCli::Foo.new }
16
+ let(:username) { ApigeeCli.configuration.username }
17
+ let(:password) { ApigeeCli.configuration.password }
18
+ let(:file) { Rack::Test::UploadedFile.new('spec/fixtures/test.txt', 'text/plain') }
19
+
20
+ describe '#get' do
21
+ it 'performs a get via Faraday::Connection with basic auth' do
22
+ expect_any_instance_of(Faraday::Connection).to receive(:basic_auth).with(username, password)
23
+ expect_any_instance_of(Faraday::Connection).to receive :get
24
+
25
+ foo.get(foo.base_url)
26
+ end
27
+ end
28
+
29
+ describe '#upload_file' do
30
+ it 'performs a post via Faraday::Connection with basic auth' do
31
+ stub_request(:post, "http://example_user:password@foos.com/")
32
+ .with(:body => "\"{\\\"leslie\\\":\\\"knope\\\"}\"",
33
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.1'})
34
+ .to_return(:status => 200, :body => "", :headers => {})
35
+ expect_any_instance_of(Faraday::Connection).to receive(:basic_auth).with(username, password)
36
+ expect_any_instance_of(Faraday::Connection).to receive :post
37
+
38
+ foo.upload_file(foo.base_url, file)
39
+ end
40
+
41
+ it 'appends the right headers to the request' do
42
+ stub_request(:post, "http://example_user:password@foos.com/")
43
+ .with(:body => "This is a test.\n",
44
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'16', 'Content-Type'=>'application/octet-stream', 'User-Agent'=>'Faraday v0.9.1'})
45
+ .to_return(:status => 200, :body => "", :headers => {})
46
+
47
+ result = foo.upload_file(foo.base_url, file)
48
+ request_headers = result.env.request_headers
49
+
50
+ expect(request_headers['Content-Type']).to eq 'application/octet-stream'
51
+ expect(request_headers['Content-Length']).to eq File.size(file).to_s
52
+ # TODO: check request body? it is currently an empty string...
53
+ end
54
+ end
55
+
56
+ describe '#post' do
57
+ it 'performs a post via Faraday::Connection with basic auth' do
58
+ expect_any_instance_of(Faraday::Connection).to receive(:basic_auth).with(username, password)
59
+ expect_any_instance_of(Faraday::Connection).to receive :post
60
+
61
+ foo.post(foo.base_url, { leslie: 'knope' }.to_json)
62
+ end
63
+
64
+ it 'appends the right headers to the request' do
65
+ result = foo.post(foo.base_url, { leslie: 'knope' }.to_json)
66
+ request_headers = result.env.request_headers
67
+
68
+ expect(request_headers['Content-Type']).to eq 'application/json'
69
+ end
70
+ end
71
+
72
+ describe '#delete' do
73
+ it 'performs a delete via Faraday::Connection with basic auth' do
74
+ expect_any_instance_of(Faraday::Connection).to receive(:basic_auth).with(username, password)
75
+ expect_any_instance_of(Faraday::Connection).to receive :delete
76
+
77
+ foo.delete(foo.base_url)
78
+ end
79
+
80
+ # TODO: should delete be doing more?
81
+ end
82
+ end
@@ -0,0 +1,202 @@
1
+ require 'spec_helper'
2
+ require 'apigee_cli/cli/app_config'
3
+
4
+ describe AppConfig do
5
+ describe 'apigee config list' do
6
+ it 'prints all the key value maps' do
7
+ app_config = AppConfig.new([])
8
+ app_config.shell = ShellRecorder.new
9
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:list_configs).and_return({
10
+ "keyValueMap" => [
11
+ { "entry" => [ { "name" => "key_one", "value" => "value_one" },
12
+ { "name" => "key_two", "value" => "value_two" }
13
+ ],
14
+ "name" => "configuration"
15
+ }
16
+ ]
17
+ })
18
+
19
+ app_config.invoke(:list)
20
+
21
+ expect(app_config.shell.printed).to eq [
22
+ "Environment: test, Config Name: configuration",
23
+ " key_one: value_one",
24
+ " key_two: value_two"
25
+ ]
26
+ end
27
+
28
+ it 'prints the key value pairs for --config_name' do
29
+ app_config = AppConfig.new([], config_name: 'teehee')
30
+ app_config.shell = ShellRecorder.new
31
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:read_config).with('teehee').and_return({
32
+ "entry" => [ { "name" => "key_a", "value" => "value_a" },
33
+ { "name" => "key_b", "value" => "value_b" }
34
+ ],
35
+ "name" => "teehee"
36
+ })
37
+
38
+ app_config.invoke(:list)
39
+
40
+ expect(app_config.shell.printed).to eq [
41
+ "Environment: test, Config Name: teehee",
42
+ " key_a: value_a",
43
+ " key_b: value_b"
44
+ ]
45
+ end
46
+
47
+ specify 'when config does not exist on Apigee Server, an error is rendered' do
48
+ app_config = AppConfig.new([])
49
+ app_config.shell = ShellRecorder.new
50
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:list_configs).and_raise("Map does not exist")
51
+
52
+ app_config.invoke(:list)
53
+
54
+ expect(app_config.shell.printed).to eq ["Map does not exist"]
55
+ end
56
+ end
57
+
58
+ describe 'apigee config push' do
59
+ let(:config_name) { 'test_config' }
60
+
61
+ before do
62
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:read_config).with(config_name).and_return({
63
+ "entry" => [ { "name" => "key_a", "value" => "value_a" },
64
+ { "name" => "key_b", "value" => "value_b" }
65
+ ],
66
+ "name" => config_name
67
+ })
68
+ end
69
+
70
+ context 'when key value map with --config_name doesn\'t exist' do
71
+ it 'creates a new key value map' do
72
+ app_config = AppConfig.new([], config_name: config_name)
73
+ app_config.shell = ShellRecorder.new
74
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:add_config).and_return([
75
+ :new, [:key_one]
76
+ ])
77
+
78
+ app_config.invoke(:push)
79
+
80
+ expect(app_config.shell.printed).to include "Creating new config for [#{config_name}] in [test] environment"
81
+ end
82
+ end
83
+
84
+ context 'when key value map with --config_name exists' do
85
+ context 'when a key value pair exists' do
86
+ it 'gets replaced with new key value pair if --overwrite=true' do
87
+ app_config = AppConfig.new([], config_name: config_name, overwrite: true)
88
+ app_config.shell = ShellRecorder.new
89
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:add_config).and_return([
90
+ :overwritten, [:key_a]
91
+ ])
92
+
93
+ app_config.invoke(:push, ["key_a=new_value_a"])
94
+
95
+ expect(app_config.shell.printed).to include "Overwriting existing config [#{config_name}] in [test] environment"
96
+ end
97
+ end
98
+
99
+ context 'when a key value pair doesn\'t exist' do
100
+ it 'gets added to the key value map' do
101
+ app_config = AppConfig.new([], config_name: config_name)
102
+ app_config.shell = ShellRecorder.new
103
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:add_config).and_return([
104
+ :existing, [:key_one]
105
+ ])
106
+
107
+ app_config.invoke(:push, ["key_one=value_one"])
108
+
109
+ expect(app_config.shell.printed).to include "Adding new keys [:key_one] to config [#{config_name}] in [test] environment"
110
+ end
111
+ end
112
+ end
113
+
114
+ it 'renders an error when there was an error trying to update config' do
115
+ app_config = AppConfig.new([], config_name: config_name)
116
+ app_config.shell = ShellRecorder.new
117
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:add_config).and_return([
118
+ :error, ["There was an error"]
119
+ ])
120
+
121
+ expect {
122
+ app_config.invoke(:push, ["key_a=new_value"])
123
+ }.to raise_error SystemExit
124
+
125
+ expect(app_config.shell.printed).to include "Error [There was an error] pushing config for [test_config] to [test] environment"
126
+ end
127
+ end
128
+
129
+ describe 'apigee config delete' do
130
+ let(:config_name) { ApigeeCli::ConfigSet::DEFAULT_CONFIG_NAME }
131
+ before do
132
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:list_configs).and_return({
133
+ "keyValueMap" => [
134
+ { "entry" => [ { "name" => "key_one", "value" => "value_one" },
135
+ { "name" => "key_two", "value" => "value_two" }
136
+ ],
137
+ "name" => "#{config_name}"
138
+ }
139
+ ]
140
+ })
141
+ end
142
+
143
+ context 'when user gives permission' do
144
+ it 'deletes --config_name=configuration key value map by default' do
145
+ app_config = AppConfig.new([])
146
+ app_config.shell = ShellRecorder.new
147
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:remove_config).with(config_name).and_return({
148
+ "entry" => [],
149
+ "name" => "#{config_name}"
150
+ })
151
+ allow(app_config.shell).to receive(:yes?).and_return(true)
152
+
153
+ app_config.invoke(:delete)
154
+
155
+ expect(app_config.shell.printed).to include "Config [#{config_name}] has been deleted from [test] environment"
156
+ end
157
+
158
+ it 'deletes --entry_name entry from --config_name key value map' do
159
+ entry_name = "key_one"
160
+ app_config = AppConfig.new([], entry_name: entry_name)
161
+ app_config.shell = ShellRecorder.new
162
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:remove_entry).with(config_name, entry_name).and_return({
163
+ "name" => "key_one",
164
+ "value" => "value_one"
165
+ })
166
+ allow(app_config.shell).to receive(:yes?).and_return(true)
167
+
168
+ app_config.invoke(:delete)
169
+
170
+ expect(app_config.shell.printed).to include "Entry [key_one] has been deleted from [configuration] in [test] environment"
171
+ end
172
+
173
+ it 'renders an error when there was an error updating a config on the server' do
174
+ app_config = AppConfig.new([])
175
+ app_config.shell = ShellRecorder.new
176
+ allow_any_instance_of(ApigeeCli::ConfigSet).to receive(:remove_config).and_raise("Error deleting #{config_name}")
177
+ allow(app_config.shell).to receive(:yes?).and_return(true)
178
+
179
+ app_config.invoke(:delete)
180
+
181
+ expect(app_config.shell.printed).to include "Error deleting #{config_name}"
182
+ end
183
+ end
184
+
185
+ context 'when user doesn\'t give permission' do
186
+ it 'doesn\'t delete the --config_name key value map' do
187
+ app_config = AppConfig.new([])
188
+ app_config.shell = ShellRecorder.new
189
+ allow(app_config.shell).to receive(:yes?).and_return(false)
190
+
191
+ expect_any_instance_of(ApigeeCli::ConfigSet).to_not receive :remove_config
192
+
193
+ expect {
194
+ app_config.invoke(:delete)
195
+ }.to raise_error SystemExit
196
+ end
197
+
198
+ it 'doesn\'t delete the --entry_name entry from --config_name key value map' do
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+ require 'apigee_cli/cli/resource'
3
+
4
+ describe Resource do
5
+ describe 'apigee resource list' do
6
+ it 'prints the names of the files, by default' do
7
+ resource = Resource.new([])
8
+ resource.shell = ShellRecorder.new
9
+ allow_any_instance_of(ApigeeCli::ResourceFile).to receive(:all).and_return({
10
+ "resourceFile" => [
11
+ { "name" => "lodash.js", "type" => "jsc" },
12
+ { "name" => "honeybadger.js", "type" => "jsc" }
13
+ ]
14
+ })
15
+
16
+ resource.invoke(:list)
17
+
18
+ expect(resource.shell.printed).to eq [
19
+ "Resource files for bellycard",
20
+ " jsc file - lodash.js",
21
+ " jsc file - honeybadger.js"
22
+ ]
23
+ end
24
+
25
+ it 'prints the content of the file for the requested --name' do
26
+ resource = Resource.new([], name: 'test.js')
27
+ resource.shell = ShellRecorder.new
28
+ allow_any_instance_of(ApigeeCli::ResourceFile).to receive(:read)
29
+ .with('test.js', 'jsc')
30
+ .and_return("Hello World")
31
+
32
+ resource.invoke(:list)
33
+
34
+ expect(resource.shell.printed).to eq ["Hello World"]
35
+ end
36
+ end
37
+
38
+ describe 'apigee resource upload' do
39
+ it 'requires the --folder from which to upload files' do
40
+ resource = Resource.new([])
41
+
42
+ expect {
43
+ resource.invoke(:upload)
44
+ }.to raise_error Thor::RequiredArgumentMissingError
45
+ end
46
+
47
+ it 'uploads only .js files in --folder to the Apigee server' do
48
+ resource = Resource.new([], folder: File.expand_path('../fixtures', __dir__))
49
+ resource.shell = ShellRecorder.new
50
+ allow_any_instance_of(ApigeeCli::ResourceFile).to receive(:upload).and_return(:new_file)
51
+
52
+ resource.invoke(:upload)
53
+
54
+ expect(resource.shell.printed).to_not include 'Creating resource for test.txt'
55
+ expect(resource.shell.printed).to eq [
56
+ "Creating resource for test.js",
57
+ "Creating resource for test2.js"
58
+ ]
59
+ end
60
+
61
+ specify 'when the file exists, it deletes it before uploading' do
62
+ resource = Resource.new([], folder: File.expand_path('../fixtures', __dir__))
63
+ resource.shell = ShellRecorder.new
64
+ allow_any_instance_of(ApigeeCli::ResourceFile).to receive(:upload).and_return(:overwritten)
65
+
66
+ resource.invoke(:upload)
67
+
68
+ expect(resource.shell.printed).to eq [
69
+ "Deleting current resource for test.js",
70
+ "Deleting current resource for test2.js"
71
+ ]
72
+ end
73
+ end
74
+
75
+ describe 'apigee resource delete' do
76
+ it 'requires the --name of the file to delete' do
77
+ resource = Resource.new([])
78
+
79
+ expect {
80
+ resource.invoke(:delete)
81
+ }.to raise_error Thor::RequiredArgumentMissingError
82
+ end
83
+
84
+ context 'when user gives permission to delete the file' do
85
+ before do
86
+ allow_any_instance_of(ShellRecorder).to receive(:yes?).and_return true
87
+ end
88
+
89
+ it 'deletes the file if it exists' do
90
+ resource = Resource.new([], name: 'test3.js')
91
+ resource.shell = ShellRecorder.new
92
+ allow_any_instance_of(ApigeeCli::ResourceFile).to receive(:remove)
93
+ .with('test3.js', 'jsc')
94
+ .and_return("Deleted")
95
+
96
+ resource.invoke(:delete)
97
+
98
+ expect(resource.shell.printed).to eq [
99
+ "Deleting current resource for test3.js"
100
+ ]
101
+ end
102
+
103
+ it 'renders an error when the file doesn\'t exist on the server' do
104
+ resource = Resource.new([], name: 'test3.js')
105
+ resource.shell = ShellRecorder.new
106
+ allow_any_instance_of(ApigeeCli::ResourceFile).to receive(:remove).and_raise("Resource already exists")
107
+
108
+ expect {
109
+ resource.invoke(:delete)
110
+ }.to raise_error SystemExit
111
+
112
+ expect(resource.shell.printed).to include "Resource already exists"
113
+ end
114
+ end
115
+
116
+ context 'when user doesn\'t give permission to delete the file' do
117
+ before do
118
+ allow_any_instance_of(ShellRecorder).to receive(:yes?).and_return false
119
+ end
120
+
121
+ it 'doesn\'t delete the file' do
122
+ resource = Resource.new([], name: 'test3.js')
123
+ resource.shell = ShellRecorder.new
124
+
125
+ expect_any_instance_of(ApigeeCli::ResourceFile).to_not receive(:remove)
126
+
127
+ expect {
128
+ resource.invoke(:delete)
129
+ }.to raise_error SystemExit
130
+ end
131
+ end
132
+ end
133
+ end