admincredible 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -0
- data/Gemfile.lock +86 -0
- data/Rakefile +24 -0
- data/Readme.md +121 -0
- data/TODOS +1 -0
- data/admincredible.gemspec +35 -0
- data/developer_notes/README.md +2 -0
- data/developer_notes/TODOS.md +0 -0
- data/lib/admincredible.rb +1 -0
- data/lib/admincredible/client.rb +60 -0
- data/lib/admincredible/configuration.rb +125 -0
- data/lib/admincredible/connection.rb +30 -0
- data/lib/admincredible/core_ext/string.rb +16 -0
- data/lib/admincredible/joomla_request.rb +22 -0
- data/lib/admincredible/middleware/logger.rb +51 -0
- data/lib/admincredible/middleware/step.rb +44 -0
- data/lib/admincredible/request.rb +47 -0
- data/lib/admincredible/resource.rb +20 -0
- data/lib/admincredible/resources/Backup.rb +34 -0
- data/lib/admincredible/resources/Compatibility.rb +13 -0
- data/lib/admincredible/resources/Extension.rb +37 -0
- data/lib/admincredible/resources/Oauth.rb +53 -0
- data/lib/admincredible/resources/Setting.rb +21 -0
- data/lib/admincredible/resources/Site.rb +9 -0
- data/lib/admincredible/resources/Upgrade.rb +64 -0
- data/spec/core/client_spec.rb +24 -0
- data/spec/core/resource_spec.rb +13 -0
- data/spec/fixtures/cassettes/backups/create.yml +73 -0
- data/spec/fixtures/cassettes/backups/start.yml +38 -0
- data/spec/fixtures/cassettes/backups/step.yml +38 -0
- data/spec/fixtures/cassettes/compatibility/added_exceptions.yml +38 -0
- data/spec/fixtures/cassettes/compatibility/status.yml +38 -0
- data/spec/fixtures/cassettes/extensions/all.yml +38 -0
- data/spec/fixtures/cassettes/extensions/update.yml +38 -0
- data/spec/fixtures/cassettes/settings/all.yml +38 -0
- data/spec/fixtures/cassettes/settings/created.yml +39 -0
- data/spec/fixtures/cassettes/settings/first.yml +38 -0
- data/spec/fixtures/cassettes/settings/updated.yml +39 -0
- data/spec/fixtures/cassettes/site/configuration.yml +41 -0
- data/spec/fixtures/cassettes/upgrade/cleanup.yml +38 -0
- data/spec/fixtures/cassettes/upgrade/create.yml +249 -0
- data/spec/fixtures/cassettes/upgrade/create_restoration_file.yml +38 -0
- data/spec/fixtures/cassettes/upgrade/download.yml +38 -0
- data/spec/fixtures/cassettes/upgrade/finalize.yml +38 -0
- data/spec/fixtures/cassettes/upgrade/info.yml +38 -0
- data/spec/fixtures/cassettes/upgrade/start.yml +39 -0
- data/spec/fixtures/cassettes/upgrade/step.yml +38 -0
- data/spec/resources/backups_spec.rb +36 -0
- data/spec/resources/compatibility_spec.rb +25 -0
- data/spec/resources/extensions_spec.rb +25 -0
- data/spec/resources/setting_spec.rb +47 -0
- data/spec/resources/site_spec.rb +14 -0
- data/spec/resources/upgrade_spec.rb +91 -0
- data/spec/spec_helper.rb +38 -0
- metadata +353 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Connection
|
3
|
+
def connection
|
4
|
+
@connection ||= build_connection
|
5
|
+
return @connection
|
6
|
+
end
|
7
|
+
|
8
|
+
def rebuild_connection
|
9
|
+
@connection = build_connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_connection
|
13
|
+
Faraday.new(config.connection_options) do |builder|
|
14
|
+
# Response
|
15
|
+
builder.use Faraday::Response::RaiseError
|
16
|
+
builder.use Admincredible::Middleware::Response::Logger, config.logger if config.logger
|
17
|
+
builder.response :json, :content_type => 'application/json'
|
18
|
+
|
19
|
+
# Request
|
20
|
+
builder.use Faraday::Request::OAuth, config.oauth_options if config.oauth?
|
21
|
+
builder.request :multipart
|
22
|
+
builder.request :json
|
23
|
+
builder.request :retry, 2 unless config.oauth?
|
24
|
+
builder.request :step unless config.oauth?
|
25
|
+
|
26
|
+
builder.adapter config.adapter || Faraday.default_adapter
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class String
|
2
|
+
##
|
3
|
+
# Transforms a string into a module name.
|
4
|
+
#
|
5
|
+
# We at String Transforming Enterprise, Inc, TM, LLC, Corp, believe in three things;
|
6
|
+
# 1. All strings should be allowed to be whatever string they like.
|
7
|
+
# 2. ??
|
8
|
+
# 3. Profit
|
9
|
+
#
|
10
|
+
def modulize
|
11
|
+
self.gsub(/__(.?)/){ "::#{$1.upcase}" }.
|
12
|
+
gsub(/\/(.?)/){ "::#{$1.upcase}" }.
|
13
|
+
gsub(/(?:_+|-+)([a-z])/){ $1.upcase }.
|
14
|
+
gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Due to the fact Joomla cant handle decent URLs (at least not out of the box)
|
2
|
+
# we abstract the requests further. all these methods take a resource + task and then any other
|
3
|
+
# has GET request vars, finally it takes normal options.
|
4
|
+
module Admincredible
|
5
|
+
module JoomlaRequest
|
6
|
+
def jget(resource, task, options={})
|
7
|
+
get('', {:resource => resource, :task => task}.merge(options))
|
8
|
+
end
|
9
|
+
|
10
|
+
def jpost(resource, task, options={})
|
11
|
+
post('', options, {:resource => resource, :task => task})
|
12
|
+
end
|
13
|
+
|
14
|
+
def jput(resource, task, options={})
|
15
|
+
put('', options, {:resource => resource, :task => task})
|
16
|
+
end
|
17
|
+
|
18
|
+
def jdelete(resource, task, options={})
|
19
|
+
delete('', {:resource => resource, :task => task}.merge(options))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'faraday_middleware/response_middleware'
|
2
|
+
|
3
|
+
module Admincredible
|
4
|
+
module Middleware
|
5
|
+
module Response
|
6
|
+
##
|
7
|
+
# So you're sitting there and you're staring off into the abyss of your terminal,
|
8
|
+
# the solarized green color scheme begins to sink into your retinas and the anger begins to build.
|
9
|
+
# This class prevents hulk moments from occurring and automagically logs requests in when enable via config.
|
10
|
+
#
|
11
|
+
class Logger < Faraday::Response::Middleware
|
12
|
+
def initialize(app, logger = nil)
|
13
|
+
super(app)
|
14
|
+
@logger = logger || begin
|
15
|
+
require 'logger'
|
16
|
+
::Logger.new(STDOUT)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
@logger.info "#{env[:method]} #{env[:url].to_s}"
|
22
|
+
@logger.debug dump_debug(env, :request_headers)
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_complete(env)
|
27
|
+
@logger.info("Status #{env[:status].to_s}")
|
28
|
+
@logger.debug dump_debug(env, :response_headers)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
##
|
34
|
+
# Dumps things, but safely, like a cat diligently using the litter box.
|
35
|
+
# It won't dump things that are nil and will make sure to inspect the objects first,
|
36
|
+
# if they are found to be carrying in countraband they'll be mapped and joined; which is probably code for
|
37
|
+
# something rather sinister.
|
38
|
+
# Remember cats, sand irst (safety), dump second,
|
39
|
+
#
|
40
|
+
def dump_debug(env, headers_key)
|
41
|
+
info = env[headers_key].map { |k, v| " #{k}: #{v.inspect}" }.join("\n")
|
42
|
+
unless env[:body].nil?
|
43
|
+
info.concat("\n")
|
44
|
+
info.concat(env[:body].inspect)
|
45
|
+
end
|
46
|
+
info
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'faraday/middleware'
|
2
|
+
|
3
|
+
module Admincredible
|
4
|
+
module Middleware
|
5
|
+
module Request
|
6
|
+
##
|
7
|
+
# Akeeba powered restoration scripts use a step/ping methodology that avoids timing out servers.
|
8
|
+
# This will automatically send more requests when a step response is received.
|
9
|
+
#
|
10
|
+
# It sadly doesn't work with OAuth yet because the re-signing requests problem was too difficult for this dev too
|
11
|
+
# solve at the time of this writing.
|
12
|
+
#
|
13
|
+
# @todo Fix this for OAuth
|
14
|
+
#
|
15
|
+
# If many months pass before this is resolved you can blame the developer's intense devotion to getting other things
|
16
|
+
# done. You can probably motivate him to fix it for oauth by bribing him with books.
|
17
|
+
#
|
18
|
+
class Step < Faraday::Middleware
|
19
|
+
DEFAULT_RETRY_AFTER = 1
|
20
|
+
|
21
|
+
def initialize(app, options={})
|
22
|
+
super(app)
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(env)
|
26
|
+
response = @app.call(env)
|
27
|
+
|
28
|
+
if response.env[:status] == 202
|
29
|
+
seconds_left = DEFAULT_RETRY_AFTER
|
30
|
+
|
31
|
+
seconds_left.times do |i|
|
32
|
+
sleep 1
|
33
|
+
end
|
34
|
+
|
35
|
+
call(env)
|
36
|
+
else
|
37
|
+
response
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
Faraday.register_middleware :request, :step => Admincredible::Middleware::Request::Step
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Request
|
3
|
+
# Perform an HTTP GET request
|
4
|
+
def get(path, options={})
|
5
|
+
request(:get, path, options)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Perform an HTTP POST request
|
9
|
+
def post(path, options={}, path_options={})
|
10
|
+
request(:post, path, options, path_options)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Perform an HTTP PUT request
|
14
|
+
def put(path, options={}, path_options={})
|
15
|
+
request(:put, path, options, path_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Perform an HTTP DELETE request
|
19
|
+
def delete(path, options={})
|
20
|
+
request(:delete, path, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def request(method, path, options={}, path_options={})
|
26
|
+
response = false
|
27
|
+
begin
|
28
|
+
response = connection.send(method) do |request|
|
29
|
+
case method
|
30
|
+
when :get, :delete
|
31
|
+
request.url(path, options)
|
32
|
+
when :post, :put
|
33
|
+
request.path = path
|
34
|
+
request.params = path_options
|
35
|
+
request.body = options unless options.empty?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue Faraday::Error::ClientError => e
|
39
|
+
response = e.response
|
40
|
+
end
|
41
|
+
|
42
|
+
return response.body if response.respond_to?(:body)
|
43
|
+
return response[:body] if response.is_a?(Hash)
|
44
|
+
return response
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Admincredible
|
2
|
+
class Resource
|
3
|
+
attr_reader :client
|
4
|
+
|
5
|
+
##
|
6
|
+
# Initializes with a client. You can use resource classes directly by passing and instance of Admincredible::Client
|
7
|
+
#
|
8
|
+
def initialize(client, options={})
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def connection
|
13
|
+
return client.connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def config
|
17
|
+
return client.config
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Resources
|
3
|
+
class Backup < Resource
|
4
|
+
def start
|
5
|
+
client.jget('backups', 'start')
|
6
|
+
end
|
7
|
+
|
8
|
+
def step(repeat_until_done=true)
|
9
|
+
response = client.jget('backups', 'step')
|
10
|
+
|
11
|
+
if response['status'] == 'working' && repeat_until_done
|
12
|
+
seconds_left = config.step_after_seconds
|
13
|
+
|
14
|
+
seconds_left.times do |i|
|
15
|
+
sleep 1
|
16
|
+
end
|
17
|
+
|
18
|
+
return step
|
19
|
+
end
|
20
|
+
|
21
|
+
response
|
22
|
+
end
|
23
|
+
|
24
|
+
def create
|
25
|
+
response = start
|
26
|
+
if response['status'] == 302
|
27
|
+
return step
|
28
|
+
else
|
29
|
+
return response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Resources
|
3
|
+
class Compatibility < Resource
|
4
|
+
def status
|
5
|
+
client.jget('compatibility', 'status')['data']
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_exceptions
|
9
|
+
client.jget('compatibility', 'add_exceptions', {:accept_terms => true})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Resources
|
3
|
+
class Extension < Resource
|
4
|
+
def all
|
5
|
+
client.jget('extensions', 'index')['data']['extensions']
|
6
|
+
end
|
7
|
+
alias :index :all
|
8
|
+
|
9
|
+
def update(extension_ids=[], repeat_until_response=true)
|
10
|
+
unless config.oauth?
|
11
|
+
client.rebuild_connection
|
12
|
+
connection.builder.request :retry, 18
|
13
|
+
end
|
14
|
+
|
15
|
+
response = nil
|
16
|
+
begin
|
17
|
+
response = client.jget('extensions', 'update', :extension_ids => extension_ids)
|
18
|
+
rescue Faraday::Error::TimeoutError => e
|
19
|
+
if repeat_until_response && config.oauth?
|
20
|
+
seconds_left = config.step_after_seconds
|
21
|
+
|
22
|
+
seconds_left.times do |i|
|
23
|
+
sleep 1
|
24
|
+
end
|
25
|
+
|
26
|
+
return update(extension_ids)
|
27
|
+
else
|
28
|
+
response = false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
client.rebuild_connection unless config.oauth?
|
33
|
+
response
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Resources
|
3
|
+
class Oauth < Resource
|
4
|
+
def register
|
5
|
+
response = client.jget('oauth', 'register')
|
6
|
+
return response unless response['status'] == 200
|
7
|
+
|
8
|
+
config.consumer_key = response['data']['osr_consumer_key']
|
9
|
+
config.consumer_secret = response['data']['osr_consumer_secret']
|
10
|
+
|
11
|
+
client.rebuild_connection
|
12
|
+
return response['data']
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_token
|
16
|
+
response = client.jget('oauth', 'request_token')
|
17
|
+
return response unless response['status'] == 200
|
18
|
+
|
19
|
+
config.token = response['data']['request_token']['oauth_token']
|
20
|
+
config.token_secret = response['data']['request_token']['oauth_token_secret']
|
21
|
+
|
22
|
+
client.rebuild_connection
|
23
|
+
return response['data']['request_token']
|
24
|
+
end
|
25
|
+
|
26
|
+
def authorization_url
|
27
|
+
url = config.url.gsub(config.direct_endpoint, config.main_endpoint)
|
28
|
+
url = URI.parse(url)
|
29
|
+
params = CGI.parse(url.query)
|
30
|
+
|
31
|
+
return ROAuth.sign_url(config.oauth_options, url.scheme + '://' + url.host + url.path, params.merge({:oauth_callback => config.oauth_callback}))
|
32
|
+
end
|
33
|
+
|
34
|
+
def access_token(oauth_verifier)
|
35
|
+
params = {
|
36
|
+
:resource => 'oauth',
|
37
|
+
:task => 'access_token',
|
38
|
+
:oauth_token => config.token,
|
39
|
+
:oauth_verifier => oauth_verifier
|
40
|
+
}
|
41
|
+
oauth_header = ROAuth.header(config.oauth_options, config.url, params)
|
42
|
+
response = JSON.parse(Nestful.get(config.url, :params => params, :headers => {'Accept' => 'application/json', 'Authorization' => oauth_header}))
|
43
|
+
return response unless response['status'] == 200
|
44
|
+
|
45
|
+
config.token = response['data']['access_token']['oauth_token']
|
46
|
+
config.token_secret = response['data']['access_token']['oauth_token_secret']
|
47
|
+
|
48
|
+
client.rebuild_connection
|
49
|
+
return response['data']['access_token']
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Resources
|
3
|
+
class Setting < Resource
|
4
|
+
def all
|
5
|
+
client.jget('settings', 'index')['data']
|
6
|
+
end
|
7
|
+
|
8
|
+
def first
|
9
|
+
client.jget('settings', 'read')['data']
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(*args)
|
13
|
+
client.jpost('settings', 'create', args.extract_options!)['data']
|
14
|
+
end
|
15
|
+
|
16
|
+
def update(*args)
|
17
|
+
client.jpost('settings', 'update', args.extract_options!)['data']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Admincredible
|
2
|
+
module Resources
|
3
|
+
class Upgrade < Resource
|
4
|
+
def info
|
5
|
+
client.jget('upgrade', 'info')['data']
|
6
|
+
end
|
7
|
+
|
8
|
+
def download
|
9
|
+
client.jget('upgrade', 'download')
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_restoration_file
|
13
|
+
client.jget('upgrade', 'create_restoration_file')
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
client.jget('upgrade', 'start')
|
18
|
+
end
|
19
|
+
|
20
|
+
def step(repeat_until_done=true)
|
21
|
+
response = client.jget('upgrade', 'step')
|
22
|
+
|
23
|
+
if response['status'] == 'working' && repeat_until_done
|
24
|
+
seconds_left = config.step_after_seconds
|
25
|
+
|
26
|
+
seconds_left.times do |i|
|
27
|
+
sleep 1
|
28
|
+
end
|
29
|
+
|
30
|
+
return step
|
31
|
+
end
|
32
|
+
|
33
|
+
response
|
34
|
+
end
|
35
|
+
|
36
|
+
def finalize
|
37
|
+
client.jget('upgrade', 'finalize')
|
38
|
+
end
|
39
|
+
|
40
|
+
def cleanup
|
41
|
+
client.jget('upgrade', 'cleanup')
|
42
|
+
end
|
43
|
+
|
44
|
+
def create
|
45
|
+
response = download
|
46
|
+
return response unless response['status'] == 200
|
47
|
+
|
48
|
+
response = create_restoration_file
|
49
|
+
return response unless response['status'] == 200
|
50
|
+
|
51
|
+
response = start
|
52
|
+
return response unless response['status'] == 302
|
53
|
+
|
54
|
+
response = step
|
55
|
+
return response unless response['status'] == 200
|
56
|
+
|
57
|
+
response = finalize
|
58
|
+
return response unless response['status'] == 200
|
59
|
+
|
60
|
+
return cleanup
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|